Pier Angelo Vendrame pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
-
c3d2496b
by Pier Angelo Vendrame at 2023-07-27T21:07:52+02:00
-
642df684
by Pier Angelo Vendrame at 2023-07-27T21:07:53+02:00
-
9e825b61
by Pier Angelo Vendrame at 2023-07-27T21:07:53+02:00
-
8061f810
by Pier Angelo Vendrame at 2023-07-27T21:07:53+02:00
-
f5a3f4af
by Pier Angelo Vendrame at 2023-07-27T21:07:54+02:00
-
71b08c4e
by Pier Angelo Vendrame at 2023-07-27T21:07:54+02:00
-
e1a69b4e
by Pier Angelo Vendrame at 2023-07-27T21:07:55+02:00
-
d457b6f8
by Pier Angelo Vendrame at 2023-07-31T20:42:54+02:00
-
c4e8cd0f
by Pier Angelo Vendrame at 2023-07-31T20:42:57+02:00
13 changed files:
- browser/components/torpreferences/content/connectionPane.js
- browser/installer/package-manifest.in
- browser/modules/TorStrings.jsm
- toolkit/components/tor-launcher/TorLauncherUtil.sys.mjs
- toolkit/components/tor-launcher/TorMonitorService.sys.mjs
- toolkit/components/tor-launcher/TorParsers.sys.mjs
- toolkit/components/tor-launcher/TorProcess.sys.mjs
- toolkit/components/tor-launcher/TorProtocolService.sys.mjs
- toolkit/torbutton/chrome/content/torbutton.js
- toolkit/torbutton/components.conf
- − toolkit/torbutton/modules/TorbuttonStartupObserver.jsm
- toolkit/torbutton/moz.build
- − toolkit/torbutton/torbutton.manifest
Changes:
| ... | ... | @@ -14,8 +14,11 @@ const { setTimeout, clearTimeout } = ChromeUtils.import( |
| 14 | 14 | const { TorSettings, TorSettingsTopics, TorSettingsData, TorBridgeSource } =
|
| 15 | 15 | ChromeUtils.import("resource:///modules/TorSettings.jsm");
|
| 16 | 16 | |
| 17 | -const { TorProtocolService } = ChromeUtils.import(
|
|
| 18 | - "resource://gre/modules/TorProtocolService.jsm"
|
|
| 17 | +const { TorParsers } = ChromeUtils.importESModule(
|
|
| 18 | + "resource://gre/modules/TorParsers.sys.mjs"
|
|
| 19 | +);
|
|
| 20 | +const { TorProtocolService } = ChromeUtils.importESModule(
|
|
| 21 | + "resource://gre/modules/TorProtocolService.sys.mjs"
|
|
| 19 | 22 | );
|
| 20 | 23 | const { TorMonitorService, TorMonitorTopics } = ChromeUtils.import(
|
| 21 | 24 | "resource://gre/modules/TorMonitorService.jsm"
|
| ... | ... | @@ -495,7 +498,7 @@ const gConnectionPane = (function () { |
| 495 | 498 | });
|
| 496 | 499 | const idString = TorStrings.settings.bridgeId;
|
| 497 | 500 | const id = card.querySelector(selectors.bridges.cardId);
|
| 498 | - const details = parseBridgeLine(bridgeString);
|
|
| 501 | + const details = TorParsers.parseBridgeLine(bridgeString);
|
|
| 499 | 502 | if (details && details.id !== undefined) {
|
| 500 | 503 | card.setAttribute("data-bridge-id", details.id);
|
| 501 | 504 | }
|
| ... | ... | @@ -1111,23 +1114,3 @@ function makeBridgeId(bridgeString) { |
| 1111 | 1114 | hash & 0x000000ff,
|
| 1112 | 1115 | ];
|
| 1113 | 1116 | } |
| 1114 | - |
|
| 1115 | -function parseBridgeLine(line) {
|
|
| 1116 | - const re =
|
|
| 1117 | - /^\s*(\S+\s+)?([0-9a-fA-F\.\[\]\:]+:\d{1,5})(\s+[0-9a-fA-F]{40})?(\s+.+)?/;
|
|
| 1118 | - const matches = line.match(re);
|
|
| 1119 | - if (!matches) {
|
|
| 1120 | - return null;
|
|
| 1121 | - }
|
|
| 1122 | - let bridge = { addr: matches[2] };
|
|
| 1123 | - if (matches[1] !== undefined) {
|
|
| 1124 | - bridge.transport = matches[1].trim();
|
|
| 1125 | - }
|
|
| 1126 | - if (matches[3] !== undefined) {
|
|
| 1127 | - bridge.id = matches[3].trim().toUpperCase();
|
|
| 1128 | - }
|
|
| 1129 | - if (matches[4] !== undefined) {
|
|
| 1130 | - bridge.args = matches[4].trim();
|
|
| 1131 | - }
|
|
| 1132 | - return bridge;
|
|
| 1133 | -} |
| ... | ... | @@ -228,7 +228,6 @@ |
| 228 | 228 | @RESPATH@/components/tor-launcher.manifest
|
| 229 | 229 | @RESPATH@/chrome/torbutton.manifest
|
| 230 | 230 | @RESPATH@/chrome/torbutton/*
|
| 231 | -@RESPATH@/components/torbutton.manifest
|
|
| 232 | 231 | @RESPATH@/chrome/toolkit@JAREXT@
|
| 233 | 232 | @RESPATH@/chrome/toolkit.manifest
|
| 234 | 233 | #ifdef MOZ_GTK
|
| ... | ... | @@ -11,9 +11,11 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); |
| 11 | 11 | const { AppConstants } = ChromeUtils.import(
|
| 12 | 12 | "resource://gre/modules/AppConstants.jsm"
|
| 13 | 13 | );
|
| 14 | -const { getLocale } = ChromeUtils.import(
|
|
| 15 | - "resource://torbutton/modules/utils.js"
|
|
| 16 | -);
|
|
| 14 | + |
|
| 15 | +function getLocale() {
|
|
| 16 | + const locale = Services.locale.appLocaleAsBCP47;
|
|
| 17 | + return locale === "ja-JP-macos" ? "ja" : locale;
|
|
| 18 | +}
|
|
| 17 | 19 | |
| 18 | 20 | /*
|
| 19 | 21 | Tor Property String Bundle
|
| ... | ... | @@ -5,6 +5,12 @@ |
| 5 | 5 | * Tor Launcher Util JS Module
|
| 6 | 6 | *************************************************************************/
|
| 7 | 7 | |
| 8 | +const lazy = {};
|
|
| 9 | + |
|
| 10 | +ChromeUtils.defineESModuleGetters(lazy, {
|
|
| 11 | + FileUtils: "resource://gre/modules/FileUtils.sys.jsm",
|
|
| 12 | +});
|
|
| 13 | + |
|
| 8 | 14 | const kPropBundleURI = "chrome://torbutton/locale/torlauncher.properties";
|
| 9 | 15 | const kPropNamePrefix = "torlauncher.";
|
| 10 | 16 | const kIPCDirPrefName = "extensions.torlauncher.tmp_ipc_dir";
|
| ... | ... | @@ -209,14 +215,15 @@ class TorFile { |
| 209 | 215 | // and return a file object. The control and SOCKS IPC objects will be
|
| 210 | 216 | // created by tor.
|
| 211 | 217 | normalize() {
|
| 212 | - if (!this.file.exists() && !this.isIPC) {
|
|
| 218 | + if (this.file.exists()) {
|
|
| 219 | + try {
|
|
| 220 | + this.file.normalize();
|
|
| 221 | + } catch (e) {
|
|
| 222 | + console.warn("Normalization of the path failed", e);
|
|
| 223 | + }
|
|
| 224 | + } else if (!this.isIPC) {
|
|
| 213 | 225 | throw new Error(`${this.fileType} file not found: ${this.file.path}`);
|
| 214 | 226 | }
|
| 215 | - try {
|
|
| 216 | - this.file.normalize();
|
|
| 217 | - } catch (e) {
|
|
| 218 | - console.warn("Normalization of the path failed", e);
|
|
| 219 | - }
|
|
| 220 | 227 | |
| 221 | 228 | // Ensure that the IPC path length is short enough for use by the
|
| 222 | 229 | // operating system. If not, create and use a unique directory under
|
| ... | ... | @@ -452,6 +459,154 @@ export const TorLauncherUtil = Object.freeze({ |
| 452 | 459 | return result ? result : "";
|
| 453 | 460 | },
|
| 454 | 461 | |
| 462 | + /**
|
|
| 463 | + * Determine what kind of SOCKS port has been requested for this session or
|
|
| 464 | + * the browser has been configured for.
|
|
| 465 | + * On Windows (where Unix domain sockets are not supported), TCP is always
|
|
| 466 | + * used.
|
|
| 467 | + *
|
|
| 468 | + * The following environment variables are supported and take precedence over
|
|
| 469 | + * preferences:
|
|
| 470 | + * TOR_TRANSPROXY (do not use a proxy)
|
|
| 471 | + * TOR_SOCKS_IPC_PATH (file system path; ignored on Windows)
|
|
| 472 | + * TOR_SOCKS_HOST
|
|
| 473 | + * TOR_SOCKS_PORT
|
|
| 474 | + *
|
|
| 475 | + * The following preferences are consulted:
|
|
| 476 | + * network.proxy.socks
|
|
| 477 | + * network.proxy.socks_port
|
|
| 478 | + * extensions.torlauncher.socks_port_use_ipc (Boolean)
|
|
| 479 | + * extensions.torlauncher.socks_ipc_path (file system path)
|
|
| 480 | + * If extensions.torlauncher.socks_ipc_path is empty, a default path is used.
|
|
| 481 | + *
|
|
| 482 | + * When using TCP, if a value is not defined via an env variable it is
|
|
| 483 | + * taken from the corresponding browser preference if possible. The
|
|
| 484 | + * exceptions are:
|
|
| 485 | + * If network.proxy.socks contains a file: URL, a default value of
|
|
| 486 | + * "127.0.0.1" is used instead.
|
|
| 487 | + * If the network.proxy.socks_port value is not valid (outside the
|
|
| 488 | + * (0; 65535] range), a default value of 9150 is used instead.
|
|
| 489 | + *
|
|
| 490 | + * The SOCKS configuration will not influence the launch of a tor daemon and
|
|
| 491 | + * the configuration of the control port in any way.
|
|
| 492 | + * When a SOCKS configuration is required without TOR_SKIP_LAUNCH, the browser
|
|
| 493 | + * will try to configure the tor instance to use the required configuration.
|
|
| 494 | + * This also applies to TOR_TRANSPROXY (at least for now): tor will be
|
|
| 495 | + * launched with its defaults.
|
|
| 496 | + *
|
|
| 497 | + * TODO: add a preference to ignore the current configuration, and let tor
|
|
| 498 | + * listen on any free port. Then, the browser will prompt the daemon the port
|
|
| 499 | + * to use through the control port (even though this is quite dangerous at the
|
|
| 500 | + * moment, because with network disabled tor will disable also the SOCKS
|
|
| 501 | + * listeners, so it means that we will have to check it every time we change
|
|
| 502 | + * the network status).
|
|
| 503 | + */
|
|
| 504 | + getPreferredSocksConfiguration() {
|
|
| 505 | + if (Services.env.exists("TOR_TRANSPROXY")) {
|
|
| 506 | + Services.prefs.setBoolPref("network.proxy.socks_remote_dns", false);
|
|
| 507 | + Services.prefs.setIntPref("network.proxy.type", 0);
|
|
| 508 | + Services.prefs.setIntPref("network.proxy.socks_port", 0);
|
|
| 509 | + Services.prefs.setCharPref("network.proxy.socks", "");
|
|
| 510 | + return { transproxy: true };
|
|
| 511 | + }
|
|
| 512 | + |
|
| 513 | + let useIPC;
|
|
| 514 | + const socksPortInfo = {
|
|
| 515 | + transproxy: false,
|
|
| 516 | + };
|
|
| 517 | + |
|
| 518 | + if (!this.isWindows && Services.env.exists("TOR_SOCKS_IPC_PATH")) {
|
|
| 519 | + useIPC = true;
|
|
| 520 | + const ipcPath = Services.env.get("TOR_SOCKS_IPC_PATH");
|
|
| 521 | + if (ipcPath) {
|
|
| 522 | + socksPortInfo.ipcFile = new lazy.FileUtils.File(ipcPath);
|
|
| 523 | + }
|
|
| 524 | + } else {
|
|
| 525 | + // Check for TCP host and port environment variables.
|
|
| 526 | + if (Services.env.exists("TOR_SOCKS_HOST")) {
|
|
| 527 | + socksPortInfo.host = Services.env.get("TOR_SOCKS_HOST");
|
|
| 528 | + useIPC = false;
|
|
| 529 | + }
|
|
| 530 | + if (Services.env.exists("TOR_SOCKS_PORT")) {
|
|
| 531 | + const port = parseInt(Services.env.get("TOR_SOCKS_PORT"), 10);
|
|
| 532 | + if (Number.isInteger(port) && port > 0 && port <= 65535) {
|
|
| 533 | + socksPortInfo.port = port;
|
|
| 534 | + useIPC = false;
|
|
| 535 | + }
|
|
| 536 | + }
|
|
| 537 | + }
|
|
| 538 | + |
|
| 539 | + if (useIPC === undefined) {
|
|
| 540 | + socksPortInfo.useIPC =
|
|
| 541 | + !this.isWindows &&
|
|
| 542 | + Services.prefs.getBoolPref(
|
|
| 543 | + "extensions.torlauncher.socks_port_use_ipc",
|
|
| 544 | + false
|
|
| 545 | + );
|
|
| 546 | + }
|
|
| 547 | + |
|
| 548 | + // Fill in missing SOCKS info from prefs.
|
|
| 549 | + if (socksPortInfo.useIPC) {
|
|
| 550 | + if (!socksPortInfo.ipcFile) {
|
|
| 551 | + socksPortInfo.ipcFile = TorLauncherUtil.getTorFile("socks_ipc", false);
|
|
| 552 | + }
|
|
| 553 | + } else {
|
|
| 554 | + if (!socksPortInfo.host) {
|
|
| 555 | + let socksAddr = Services.prefs.getCharPref(
|
|
| 556 | + "network.proxy.socks",
|
|
| 557 | + "127.0.0.1"
|
|
| 558 | + );
|
|
| 559 | + let socksAddrHasHost = socksAddr && !socksAddr.startsWith("file:");
|
|
| 560 | + socksPortInfo.host = socksAddrHasHost ? socksAddr : "127.0.0.1";
|
|
| 561 | + }
|
|
| 562 | + |
|
| 563 | + if (!socksPortInfo.port) {
|
|
| 564 | + let socksPort = Services.prefs.getIntPref(
|
|
| 565 | + "network.proxy.socks_port",
|
|
| 566 | + 0
|
|
| 567 | + );
|
|
| 568 | + // This pref is set as 0 by default in Firefox, use 9150 if we get 0.
|
|
| 569 | + socksPortInfo.port =
|
|
| 570 | + socksPort > 0 && socksPort <= 65535 ? socksPort : 9150;
|
|
| 571 | + }
|
|
| 572 | + }
|
|
| 573 | + |
|
| 574 | + return socksPortInfo;
|
|
| 575 | + },
|
|
| 576 | + |
|
| 577 | + setProxyConfiguration(socksPortInfo) {
|
|
| 578 | + if (socksPortInfo.transproxy) {
|
|
| 579 | + return;
|
|
| 580 | + }
|
|
| 581 | + |
|
| 582 | + if (socksPortInfo.useIPC) {
|
|
| 583 | + const fph = Services.io
|
|
| 584 | + .getProtocolHandler("file")
|
|
| 585 | + .QueryInterface(Ci.nsIFileProtocolHandler);
|
|
| 586 | + const fileURI = fph.newFileURI(socksPortInfo.ipcFile);
|
|
| 587 | + Services.prefs.setCharPref("network.proxy.socks", fileURI.spec);
|
|
| 588 | + Services.prefs.setIntPref("network.proxy.socks_port", 0);
|
|
| 589 | + } else {
|
|
| 590 | + if (socksPortInfo.host) {
|
|
| 591 | + Services.prefs.setCharPref("network.proxy.socks", socksPortInfo.host);
|
|
| 592 | + }
|
|
| 593 | + if (socksPortInfo.port) {
|
|
| 594 | + Services.prefs.setIntPref(
|
|
| 595 | + "network.proxy.socks_port",
|
|
| 596 | + socksPortInfo.port
|
|
| 597 | + );
|
|
| 598 | + }
|
|
| 599 | + }
|
|
| 600 | + |
|
| 601 | + if (socksPortInfo.ipcFile || socksPortInfo.host || socksPortInfo.port) {
|
|
| 602 | + Services.prefs.setBoolPref("network.proxy.socks_remote_dns", true);
|
|
| 603 | + Services.prefs.setIntPref("network.proxy.type", 1);
|
|
| 604 | + }
|
|
| 605 | + |
|
| 606 | + // Force prefs to be synced to disk
|
|
| 607 | + Services.prefs.savePrefFile(null);
|
|
| 608 | + },
|
|
| 609 | + |
|
| 455 | 610 | get shouldStartAndOwnTor() {
|
| 456 | 611 | const kPrefStartTor = "extensions.torlauncher.start_tor";
|
| 457 | 612 | try {
|
| ... | ... | @@ -13,6 +13,10 @@ import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs" |
| 13 | 13 | |
| 14 | 14 | const lazy = {};
|
| 15 | 15 | |
| 16 | +ChromeUtils.defineESModuleGetters(lazy, {
|
|
| 17 | + TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
|
|
| 18 | +});
|
|
| 19 | + |
|
| 16 | 20 | ChromeUtils.defineModuleGetter(
|
| 17 | 21 | lazy,
|
| 18 | 22 | "controller",
|
| ... | ... | @@ -233,7 +237,10 @@ export const TorMonitorService = { |
| 233 | 237 | // TorProcess should be instanced once, then always reused and restarted
|
| 234 | 238 | // only through the prompt it exposes when the controlled process dies.
|
| 235 | 239 | if (!this._torProcess) {
|
| 236 | - this._torProcess = new TorProcess();
|
|
| 240 | + this._torProcess = new TorProcess(
|
|
| 241 | + lazy.TorProtocolService.torControlPortInfo,
|
|
| 242 | + lazy.TorProtocolService.torSOCKSPortInfo
|
|
| 243 | + );
|
|
| 237 | 244 | this._torProcess.onExit = () => {
|
| 238 | 245 | this._shutDownEventMonitor();
|
| 239 | 246 | Services.obs.notifyObservers(null, TorTopics.ProcessExited);
|
| ... | ... | @@ -254,6 +261,7 @@ export const TorMonitorService = { |
| 254 | 261 | await this._torProcess.start();
|
| 255 | 262 | if (this._torProcess.isRunning) {
|
| 256 | 263 | logger.info("tor started");
|
| 264 | + this._torProcessStartTime = Date.now();
|
|
| 257 | 265 | }
|
| 258 | 266 | } catch (e) {
|
| 259 | 267 | // TorProcess already logs the error.
|
| ... | ... | @@ -267,4 +267,18 @@ export const TorParsers = Object.freeze({ |
| 267 | 267 | rv += aStr.substring(lastAdded, aStr.length - 1);
|
| 268 | 268 | return rv;
|
| 269 | 269 | },
|
| 270 | + |
|
| 271 | + parseBridgeLine(line) {
|
|
| 272 | + const re =
|
|
| 273 | + /\s*(?:(?<transport>\S+)\s+)?(?<addr>[0-9a-fA-F\.\[\]\:]+:\d{1,5})(?:\s+(?<id>[0-9a-fA-F]{40}))?(?:\s+(?<args>.+))?/;
|
|
| 274 | + const match = re.exec(line);
|
|
| 275 | + if (!match) {
|
|
| 276 | + throw new Error("Invalid bridge line.");
|
|
| 277 | + }
|
|
| 278 | + const bridge = match.groups;
|
|
| 279 | + if (!bridge.transport) {
|
|
| 280 | + bridge.transport = "vanilla";
|
|
| 281 | + }
|
|
| 282 | + return bridge;
|
|
| 283 | + },
|
|
| 270 | 284 | }); |
| 1 | +/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
| 2 | + * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
| 3 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
| 4 | + |
|
| 1 | 5 | import { setTimeout } from "resource://gre/modules/Timer.sys.mjs";
|
| 2 | 6 | import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
|
| 3 | 7 | import { Subprocess } from "resource://gre/modules/Subprocess.sys.mjs";
|
| 4 | 8 | |
| 5 | 9 | const lazy = {};
|
| 6 | 10 | |
| 7 | -ChromeUtils.defineModuleGetter(
|
|
| 8 | - lazy,
|
|
| 9 | - "TorProtocolService",
|
|
| 10 | - "resource://gre/modules/TorProtocolService.jsm"
|
|
| 11 | -);
|
|
| 12 | -const { TorLauncherUtil } = ChromeUtils.import(
|
|
| 13 | - "resource://gre/modules/TorLauncherUtil.jsm"
|
|
| 14 | -);
|
|
| 15 | - |
|
| 16 | -const { TorParsers } = ChromeUtils.import(
|
|
| 17 | - "resource://gre/modules/TorParsers.jsm"
|
|
| 18 | -);
|
|
| 11 | +ChromeUtils.defineESModuleGetters(lazy, {
|
|
| 12 | + TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
|
|
| 13 | + TorParsers: "resource://gre/modules/TorParsers.sys.mjs",
|
|
| 14 | +});
|
|
| 19 | 15 | |
| 20 | 16 | const TorProcessStatus = Object.freeze({
|
| 21 | 17 | Unknown: 0,
|
| ... | ... | @@ -30,53 +26,86 @@ const logger = new ConsoleAPI({ |
| 30 | 26 | });
|
| 31 | 27 | |
| 32 | 28 | export class TorProcess {
|
| 33 | - _exeFile = null;
|
|
| 34 | - _dataDir = null;
|
|
| 35 | - _args = [];
|
|
| 36 | - _subprocess = null;
|
|
| 37 | - _status = TorProcessStatus.Unknown;
|
|
| 38 | - _torProcessStartTime = null; // JS Date.now()
|
|
| 39 | - _didConnectToTorControlPort = false; // Have we ever made a connection?
|
|
| 29 | + #controlSettings;
|
|
| 30 | + #socksSettings;
|
|
| 31 | + #exeFile = null;
|
|
| 32 | + #dataDir = null;
|
|
| 33 | + #args = [];
|
|
| 34 | + #subprocess = null;
|
|
| 35 | + #status = TorProcessStatus.Unknown;
|
|
| 36 | + // Have we ever made a connection on the control port?
|
|
| 37 | + #didConnectToTorControlPort = false;
|
|
| 38 | + |
|
| 39 | + onExit = exitCode => {};
|
|
| 40 | + onRestart = () => {};
|
|
| 41 | + |
|
| 42 | + constructor(controlSettings, socksSettings) {
|
|
| 43 | + if (
|
|
| 44 | + controlSettings &&
|
|
| 45 | + !controlSettings.password &&
|
|
| 46 | + !controlSettings.cookieFilePath
|
|
| 47 | + ) {
|
|
| 48 | + throw new Error("Unauthenticated control port is not supported");
|
|
| 49 | + }
|
|
| 40 | 50 | |
| 41 | - onExit = null;
|
|
| 42 | - onRestart = null;
|
|
| 51 | + const checkPort = port =>
|
|
| 52 | + port === undefined ||
|
|
| 53 | + (Number.isInteger(controlSettings.port) &&
|
|
| 54 | + controlSettings.port > 0 &&
|
|
| 55 | + controlSettings.port < 65535);
|
|
| 56 | + if (!checkPort(controlSettings?.port)) {
|
|
| 57 | + throw new Error("Invalid control port");
|
|
| 58 | + }
|
|
| 59 | + if (!checkPort(socksSettings.port)) {
|
|
| 60 | + throw new Error("Invalid port specified for the SOCKS port");
|
|
| 61 | + }
|
|
| 62 | + |
|
| 63 | + this.#controlSettings = { ...controlSettings };
|
|
| 64 | + const ipcFileToString = file =>
|
|
| 65 | + "unix:" + lazy.TorParsers.escapeString(file.path);
|
|
| 66 | + if (controlSettings.ipcFile) {
|
|
| 67 | + this.#controlSettings.ipcFile = ipcFileToString(controlSettings.ipcFile);
|
|
| 68 | + }
|
|
| 69 | + this.#socksSettings = { ...socksSettings };
|
|
| 70 | + if (socksSettings.ipcFile) {
|
|
| 71 | + this.#socksSettings.ipcFile = ipcFileToString(socksSettings.ipcFile);
|
|
| 72 | + }
|
|
| 73 | + }
|
|
| 43 | 74 | |
| 44 | 75 | get status() {
|
| 45 | - return this._status;
|
|
| 76 | + return this.#status;
|
|
| 46 | 77 | }
|
| 47 | 78 | |
| 48 | 79 | get isRunning() {
|
| 49 | 80 | return (
|
| 50 | - this._status === TorProcessStatus.Starting ||
|
|
| 51 | - this._status === TorProcessStatus.Running
|
|
| 81 | + this.#status === TorProcessStatus.Starting ||
|
|
| 82 | + this.#status === TorProcessStatus.Running
|
|
| 52 | 83 | );
|
| 53 | 84 | }
|
| 54 | 85 | |
| 55 | 86 | async start() {
|
| 56 | - if (this._subprocess) {
|
|
| 87 | + if (this.#subprocess) {
|
|
| 57 | 88 | return;
|
| 58 | 89 | }
|
| 59 | 90 | |
| 60 | - this._status = TorProcessStatus.Unknown;
|
|
| 91 | + this.#status = TorProcessStatus.Unknown;
|
|
| 61 | 92 | |
| 62 | 93 | try {
|
| 63 | - this._makeArgs();
|
|
| 64 | - this._addControlPortArg();
|
|
| 65 | - this._addSocksPortArg();
|
|
| 94 | + this.#makeArgs();
|
|
| 95 | + this.#addControlPortArgs();
|
|
| 96 | + this.#addSocksPortArg();
|
|
| 66 | 97 | |
| 67 | 98 | const pid = Services.appinfo.processID;
|
| 68 | 99 | if (pid !== 0) {
|
| 69 | - this._args.push("__OwningControllerProcess");
|
|
| 70 | - this._args.push("" + pid);
|
|
| 100 | + this.#args.push("__OwningControllerProcess", pid.toString());
|
|
| 71 | 101 | }
|
| 72 | 102 | |
| 73 | - if (TorLauncherUtil.shouldShowNetworkSettings) {
|
|
| 74 | - this._args.push("DisableNetwork");
|
|
| 75 | - this._args.push("1");
|
|
| 103 | + if (lazy.TorLauncherUtil.shouldShowNetworkSettings) {
|
|
| 104 | + this.#args.push("DisableNetwork", "1");
|
|
| 76 | 105 | }
|
| 77 | 106 | |
| 78 | - this._status = TorProcessStatus.Starting;
|
|
| 79 | - this._didConnectToTorControlPort = false;
|
|
| 107 | + this.#status = TorProcessStatus.Starting;
|
|
| 108 | + this.#didConnectToTorControlPort = false;
|
|
| 80 | 109 | |
| 81 | 110 | // useful for simulating slow tor daemon launch
|
| 82 | 111 | const kPrefTorDaemonLaunchDelay = "extensions.torlauncher.launch_delay";
|
| ... | ... | @@ -88,29 +117,31 @@ export class TorProcess { |
| 88 | 117 | await new Promise(resolve => setTimeout(() => resolve(), launchDelay));
|
| 89 | 118 | }
|
| 90 | 119 | |
| 91 | - logger.debug(`Starting ${this._exeFile.path}`, this._args);
|
|
| 120 | + logger.debug(`Starting ${this.#exeFile.path}`, this.#args);
|
|
| 92 | 121 | const options = {
|
| 93 | - command: this._exeFile.path,
|
|
| 94 | - arguments: this._args,
|
|
| 122 | + command: this.#exeFile.path,
|
|
| 123 | + arguments: this.#args,
|
|
| 95 | 124 | stderr: "stdout",
|
| 96 | - workdir: TorLauncherUtil.getTorFile("pt-startup-dir", false).path,
|
|
| 125 | + workdir: lazy.TorLauncherUtil.getTorFile("pt-startup-dir", false).path,
|
|
| 97 | 126 | };
|
| 98 | - this._subprocess = await Subprocess.call(options);
|
|
| 99 | - this._dumpStdout();
|
|
| 100 | - this._watchProcess();
|
|
| 101 | - this._status = TorProcessStatus.Running;
|
|
| 102 | - this._torProcessStartTime = Date.now();
|
|
| 127 | + this.#subprocess = await Subprocess.call(options);
|
|
| 128 | + this.#status = TorProcessStatus.Running;
|
|
| 103 | 129 | } catch (e) {
|
| 104 | - this._status = TorProcessStatus.Exited;
|
|
| 105 | - this._subprocess = null;
|
|
| 130 | + this.#status = TorProcessStatus.Exited;
|
|
| 131 | + this.#subprocess = null;
|
|
| 106 | 132 | logger.error("startTor error:", e);
|
| 107 | 133 | throw e;
|
| 108 | 134 | }
|
| 135 | + |
|
| 136 | + // Do not await the following functions, as they will return only when the
|
|
| 137 | + // process exits.
|
|
| 138 | + this.#dumpStdout();
|
|
| 139 | + this.#watchProcess();
|
|
| 109 | 140 | }
|
| 110 | 141 | |
| 111 | 142 | // Forget about a process.
|
| 112 | 143 | //
|
| 113 | - // Instead of killing the tor process, we rely on the TAKEOWNERSHIP feature
|
|
| 144 | + // Instead of killing the tor process, we rely on the TAKEOWNERSHIP feature
|
|
| 114 | 145 | // to shut down tor when we close the control port connection.
|
| 115 | 146 | //
|
| 116 | 147 | // Previously, we sent a SIGNAL HALT command to the tor control port,
|
| ... | ... | @@ -123,36 +154,38 @@ export class TorProcess { |
| 123 | 154 | // Still, before closing the owning connection, this class should forget about
|
| 124 | 155 | // the process, so that future notifications will be ignored.
|
| 125 | 156 | forget() {
|
| 126 | - this._subprocess = null;
|
|
| 127 | - this._status = TorProcessStatus.Exited;
|
|
| 157 | + this.#subprocess = null;
|
|
| 158 | + this.#status = TorProcessStatus.Exited;
|
|
| 128 | 159 | }
|
| 129 | 160 | |
| 130 | 161 | // The owner of the process can use this function to tell us that they
|
| 131 | 162 | // successfully connected to the control port. This information will be used
|
| 132 | 163 | // only to decide which text to show in the confirmation dialog if tor exits.
|
| 133 | 164 | connectionWorked() {
|
| 134 | - this._didConnectToTorControlPort = true;
|
|
| 165 | + this.#didConnectToTorControlPort = true;
|
|
| 135 | 166 | }
|
| 136 | 167 | |
| 137 | - async _dumpStdout() {
|
|
| 168 | + async #dumpStdout() {
|
|
| 138 | 169 | let string;
|
| 139 | 170 | while (
|
| 140 | - this._subprocess &&
|
|
| 141 | - (string = await this._subprocess.stdout.readString())
|
|
| 171 | + this.#subprocess &&
|
|
| 172 | + (string = await this.#subprocess.stdout.readString())
|
|
| 142 | 173 | ) {
|
| 143 | 174 | dump(string);
|
| 144 | 175 | }
|
| 145 | 176 | }
|
| 146 | 177 | |
| 147 | - async _watchProcess() {
|
|
| 148 | - const watched = this._subprocess;
|
|
| 178 | + async #watchProcess() {
|
|
| 179 | + const watched = this.#subprocess;
|
|
| 149 | 180 | if (!watched) {
|
| 150 | 181 | return;
|
| 151 | 182 | }
|
| 183 | + let processExitCode;
|
|
| 152 | 184 | try {
|
| 153 | 185 | const { exitCode } = await watched.wait();
|
| 186 | + processExitCode = exitCode;
|
|
| 154 | 187 | |
| 155 | - if (watched !== this._subprocess) {
|
|
| 188 | + if (watched !== this.#subprocess) {
|
|
| 156 | 189 | logger.debug(`A Tor process exited with code ${exitCode}.`);
|
| 157 | 190 | } else if (exitCode) {
|
| 158 | 191 | logger.warn(`The watched Tor process exited with code ${exitCode}.`);
|
| ... | ... | @@ -163,30 +196,31 @@ export class TorProcess { |
| 163 | 196 | logger.error("Failed to watch the tor process", e);
|
| 164 | 197 | }
|
| 165 | 198 | |
| 166 | - if (watched === this._subprocess) {
|
|
| 167 | - this._processExitedUnexpectedly();
|
|
| 199 | + if (watched === this.#subprocess) {
|
|
| 200 | + this.#processExitedUnexpectedly(processExitCode);
|
|
| 168 | 201 | }
|
| 169 | 202 | }
|
| 170 | 203 | |
| 171 | - _processExitedUnexpectedly() {
|
|
| 172 | - this._subprocess = null;
|
|
| 173 | - this._status = TorProcessStatus.Exited;
|
|
| 204 | + #processExitedUnexpectedly(exitCode) {
|
|
| 205 | + this.#subprocess = null;
|
|
| 206 | + this.#status = TorProcessStatus.Exited;
|
|
| 174 | 207 | |
| 175 | 208 | // TODO: Move this logic somewhere else?
|
| 176 | 209 | let s;
|
| 177 | - if (!this._didConnectToTorControlPort) {
|
|
| 210 | + if (!this.#didConnectToTorControlPort) {
|
|
| 178 | 211 | // tor might be misconfigured, becauser we could never connect to it
|
| 179 | 212 | const key = "tor_exited_during_startup";
|
| 180 | - s = TorLauncherUtil.getLocalizedString(key);
|
|
| 213 | + s = lazy.TorLauncherUtil.getLocalizedString(key);
|
|
| 181 | 214 | } else {
|
| 182 | 215 | // tor exited suddenly, so configuration should be okay
|
| 183 | 216 | s =
|
| 184 | - TorLauncherUtil.getLocalizedString("tor_exited") +
|
|
| 217 | + lazy.TorLauncherUtil.getLocalizedString("tor_exited") +
|
|
| 185 | 218 | "\n\n" +
|
| 186 | - TorLauncherUtil.getLocalizedString("tor_exited2");
|
|
| 219 | + lazy.TorLauncherUtil.getLocalizedString("tor_exited2");
|
|
| 187 | 220 | }
|
| 188 | 221 | logger.info(s);
|
| 189 | - const defaultBtnLabel = TorLauncherUtil.getLocalizedString("restart_tor");
|
|
| 222 | + const defaultBtnLabel =
|
|
| 223 | + lazy.TorLauncherUtil.getLocalizedString("restart_tor");
|
|
| 190 | 224 | let cancelBtnLabel = "OK";
|
| 191 | 225 | try {
|
| 192 | 226 | const kSysBundleURI = "chrome://global/locale/commonDialogs.properties";
|
| ... | ... | @@ -196,51 +230,43 @@ export class TorProcess { |
| 196 | 230 | logger.warn("Could not localize the cancel button", e);
|
| 197 | 231 | }
|
| 198 | 232 | |
| 199 | - const restart = TorLauncherUtil.showConfirm(
|
|
| 233 | + const restart = lazy.TorLauncherUtil.showConfirm(
|
|
| 200 | 234 | null,
|
| 201 | 235 | s,
|
| 202 | 236 | defaultBtnLabel,
|
| 203 | 237 | cancelBtnLabel
|
| 204 | 238 | );
|
| 205 | 239 | if (restart) {
|
| 206 | - this.start().then(() => {
|
|
| 207 | - if (this.onRestart) {
|
|
| 208 | - this.onRestart();
|
|
| 209 | - }
|
|
| 210 | - });
|
|
| 211 | - } else if (this.onExit) {
|
|
| 212 | - this.onExit();
|
|
| 240 | + this.start().then(this.onRestart);
|
|
| 241 | + } else {
|
|
| 242 | + this.onExit(exitCode);
|
|
| 213 | 243 | }
|
| 214 | 244 | }
|
| 215 | 245 | |
| 216 | - _makeArgs() {
|
|
| 217 | - // Ideally, we would cd to the Firefox application directory before
|
|
| 218 | - // starting tor (but we don't know how to do that). Instead, we
|
|
| 219 | - // rely on the TBB launcher to start Firefox from the right place.
|
|
| 220 | - |
|
| 246 | + #makeArgs() {
|
|
| 247 | + this.#exeFile = lazy.TorLauncherUtil.getTorFile("tor", false);
|
|
| 248 | + const torrcFile = lazy.TorLauncherUtil.getTorFile("torrc", true);
|
|
| 221 | 249 | // Get the Tor data directory first so it is created before we try to
|
| 222 | 250 | // construct paths to files that will be inside it.
|
| 223 | - this._exeFile = TorLauncherUtil.getTorFile("tor", false);
|
|
| 224 | - const torrcFile = TorLauncherUtil.getTorFile("torrc", true);
|
|
| 225 | - this._dataDir = TorLauncherUtil.getTorFile("tordatadir", true);
|
|
| 226 | - const onionAuthDir = TorLauncherUtil.getTorFile("toronionauthdir", true);
|
|
| 227 | - const hashedPassword = lazy.TorProtocolService.torGetPassword(true);
|
|
| 251 | + this.#dataDir = lazy.TorLauncherUtil.getTorFile("tordatadir", true);
|
|
| 252 | + const onionAuthDir = lazy.TorLauncherUtil.getTorFile(
|
|
| 253 | + "toronionauthdir",
|
|
| 254 | + true
|
|
| 255 | + );
|
|
| 228 | 256 | let detailsKey;
|
| 229 | - if (!this._exeFile) {
|
|
| 257 | + if (!this.#exeFile) {
|
|
| 230 | 258 | detailsKey = "tor_missing";
|
| 231 | 259 | } else if (!torrcFile) {
|
| 232 | 260 | detailsKey = "torrc_missing";
|
| 233 | - } else if (!this._dataDir) {
|
|
| 261 | + } else if (!this.#dataDir) {
|
|
| 234 | 262 | detailsKey = "datadir_missing";
|
| 235 | 263 | } else if (!onionAuthDir) {
|
| 236 | 264 | detailsKey = "onionauthdir_missing";
|
| 237 | - } else if (!hashedPassword) {
|
|
| 238 | - detailsKey = "password_hash_missing";
|
|
| 239 | 265 | }
|
| 240 | 266 | if (detailsKey) {
|
| 241 | - const details = TorLauncherUtil.getLocalizedString(detailsKey);
|
|
| 267 | + const details = lazy.TorLauncherUtil.getLocalizedString(detailsKey);
|
|
| 242 | 268 | const key = "unable_to_start_tor";
|
| 243 | - const err = TorLauncherUtil.getFormattedLocalizedString(
|
|
| 269 | + const err = lazy.TorLauncherUtil.getFormattedLocalizedString(
|
|
| 244 | 270 | key,
|
| 245 | 271 | [details],
|
| 246 | 272 | 1
|
| ... | ... | @@ -248,7 +274,7 @@ export class TorProcess { |
| 248 | 274 | throw new Error(err);
|
| 249 | 275 | }
|
| 250 | 276 | |
| 251 | - const torrcDefaultsFile = TorLauncherUtil.getTorFile(
|
|
| 277 | + const torrcDefaultsFile = lazy.TorLauncherUtil.getTorFile(
|
|
| 252 | 278 | "torrc-defaults",
|
| 253 | 279 | false
|
| 254 | 280 | );
|
| ... | ... | @@ -258,77 +284,131 @@ export class TorProcess { |
| 258 | 284 | const geoip6File = torrcDefaultsFile.clone();
|
| 259 | 285 | geoip6File.leafName = "geoip6";
|
| 260 | 286 | |
| 261 | - this._args = [];
|
|
| 287 | + this.#args = [];
|
|
| 262 | 288 | if (torrcDefaultsFile) {
|
| 263 | - this._args.push("--defaults-torrc");
|
|
| 264 | - this._args.push(torrcDefaultsFile.path);
|
|
| 289 | + this.#args.push("--defaults-torrc", torrcDefaultsFile.path);
|
|
| 265 | 290 | }
|
| 266 | - this._args.push("-f");
|
|
| 267 | - this._args.push(torrcFile.path);
|
|
| 268 | - this._args.push("DataDirectory");
|
|
| 269 | - this._args.push(this._dataDir.path);
|
|
| 270 | - this._args.push("ClientOnionAuthDir");
|
|
| 271 | - this._args.push(onionAuthDir.path);
|
|
| 272 | - this._args.push("GeoIPFile");
|
|
| 273 | - this._args.push(geoipFile.path);
|
|
| 274 | - this._args.push("GeoIPv6File");
|
|
| 275 | - this._args.push(geoip6File.path);
|
|
| 276 | - this._args.push("HashedControlPassword");
|
|
| 277 | - this._args.push(hashedPassword);
|
|
| 291 | + this.#args.push("-f", torrcFile.path);
|
|
| 292 | + this.#args.push("DataDirectory", this.#dataDir.path);
|
|
| 293 | + this.#args.push("ClientOnionAuthDir", onionAuthDir.path);
|
|
| 294 | + this.#args.push("GeoIPFile", geoipFile.path);
|
|
| 295 | + this.#args.push("GeoIPv6File", geoip6File.path);
|
|
| 278 | 296 | }
|
| 279 | 297 | |
| 280 | - _addControlPortArg() {
|
|
| 281 | - // Include a ControlPort argument to support switching between
|
|
| 282 | - // a TCP port and an IPC port (e.g., a Unix domain socket). We
|
|
| 283 | - // include a "+__" prefix so that (1) this control port is added
|
|
| 284 | - // to any control ports that the user has defined in their torrc
|
|
| 285 | - // file and (2) it is never written to torrc.
|
|
| 298 | + /**
|
|
| 299 | + * Add all the arguments related to the control port.
|
|
| 300 | + * We use the + prefix so that the the port is added to any other port already
|
|
| 301 | + * defined in the torrc, and the __ prefix so that it is never written to
|
|
| 302 | + * torrc.
|
|
| 303 | + */
|
|
| 304 | + #addControlPortArgs() {
|
|
| 305 | + if (!this.#controlSettings) {
|
|
| 306 | + return;
|
|
| 307 | + }
|
|
| 308 | + |
|
| 286 | 309 | let controlPortArg;
|
| 287 | - const controlIPCFile = lazy.TorProtocolService.torGetControlIPCFile();
|
|
| 288 | - const controlPort = lazy.TorProtocolService.torGetControlPort();
|
|
| 289 | - if (controlIPCFile) {
|
|
| 290 | - controlPortArg = this._ipcPortArg(controlIPCFile);
|
|
| 291 | - } else if (controlPort) {
|
|
| 292 | - controlPortArg = "" + controlPort;
|
|
| 310 | + if (this.#controlSettings.ipcFile) {
|
|
| 311 | + controlPortArg = this.#controlSettings.ipcFile;
|
|
| 312 | + } else if (this.#controlSettings.port) {
|
|
| 313 | + controlPortArg = this.#controlSettings.host
|
|
| 314 | + ? `${this.#controlSettings.host}:${this.#controlSettings.port}`
|
|
| 315 | + : this.#controlSettings.port.toString();
|
|
| 293 | 316 | }
|
| 294 | 317 | if (controlPortArg) {
|
| 295 | - this._args.push("+__ControlPort");
|
|
| 296 | - this._args.push(controlPortArg);
|
|
| 318 | + this.#args.push("+__ControlPort", controlPortArg);
|
|
| 319 | + }
|
|
| 320 | + |
|
| 321 | + if (this.#controlSettings.password) {
|
|
| 322 | + this.#args.push(
|
|
| 323 | + "HashedControlPassword",
|
|
| 324 | + this.#hashPassword(this.#controlSettings.password)
|
|
| 325 | + );
|
|
| 326 | + }
|
|
| 327 | + if (this.#controlSettings.cookieFilePath) {
|
|
| 328 | + this.#args.push("CookieAuthentication", "1");
|
|
| 329 | + this.#args.push("CookieAuthFile", this.#controlSettings.cookieFilePath);
|
|
| 297 | 330 | }
|
| 298 | 331 | }
|
| 299 | 332 | |
| 300 | - _addSocksPortArg() {
|
|
| 301 | - // Include a SocksPort argument to support switching between
|
|
| 302 | - // a TCP port and an IPC port (e.g., a Unix domain socket). We
|
|
| 303 | - // include a "+__" prefix so that (1) this SOCKS port is added
|
|
| 304 | - // to any SOCKS ports that the user has defined in their torrc
|
|
| 305 | - // file and (2) it is never written to torrc.
|
|
| 306 | - const socksPortInfo = lazy.TorProtocolService.torGetSOCKSPortInfo();
|
|
| 307 | - if (socksPortInfo) {
|
|
| 308 | - let socksPortArg;
|
|
| 309 | - if (socksPortInfo.ipcFile) {
|
|
| 310 | - socksPortArg = this._ipcPortArg(socksPortInfo.ipcFile);
|
|
| 311 | - } else if (socksPortInfo.host && socksPortInfo.port != 0) {
|
|
| 312 | - socksPortArg = socksPortInfo.host + ":" + socksPortInfo.port;
|
|
| 313 | - }
|
|
| 314 | - if (socksPortArg) {
|
|
| 315 | - let socksPortFlags = Services.prefs.getCharPref(
|
|
| 316 | - "extensions.torlauncher.socks_port_flags",
|
|
| 317 | - "IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth"
|
|
| 318 | - );
|
|
| 319 | - if (socksPortFlags) {
|
|
| 320 | - socksPortArg += " " + socksPortFlags;
|
|
| 321 | - }
|
|
| 322 | - this._args.push("+__SocksPort");
|
|
| 323 | - this._args.push(socksPortArg);
|
|
| 333 | + /**
|
|
| 334 | + * Add the argument related to the control port.
|
|
| 335 | + * We use the + prefix so that the the port is added to any other port already
|
|
| 336 | + * defined in the torrc, and the __ prefix so that it is never written to
|
|
| 337 | + * torrc.
|
|
| 338 | + */
|
|
| 339 | + #addSocksPortArg() {
|
|
| 340 | + let socksPortArg;
|
|
| 341 | + if (this.#socksSettings.ipcFile) {
|
|
| 342 | + socksPortArg = this.#socksSettings.ipcFile;
|
|
| 343 | + } else if (this.#socksSettings.port != 0) {
|
|
| 344 | + socksPortArg = this.#socksSettings.host
|
|
| 345 | + ? `${this.#socksSettings.host}:${this.#socksSettings.port}`
|
|
| 346 | + : this.#socksSettings.port.toString();
|
|
| 347 | + }
|
|
| 348 | + if (socksPortArg) {
|
|
| 349 | + const socksPortFlags = Services.prefs.getCharPref(
|
|
| 350 | + "extensions.torlauncher.socks_port_flags",
|
|
| 351 | + "IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth"
|
|
| 352 | + );
|
|
| 353 | + if (socksPortFlags) {
|
|
| 354 | + socksPortArg += " " + socksPortFlags;
|
|
| 324 | 355 | }
|
| 356 | + this.#args.push("+__SocksPort", socksPortArg);
|
|
| 357 | + }
|
|
| 358 | + }
|
|
| 359 | + |
|
| 360 | + // Based on Vidalia's TorSettings::hashPassword().
|
|
| 361 | + #hashPassword(aHexPassword) {
|
|
| 362 | + if (!aHexPassword) {
|
|
| 363 | + return null;
|
|
| 325 | 364 | }
|
| 365 | + |
|
| 366 | + // Generate a random, 8 byte salt value.
|
|
| 367 | + const salt = Array.from(crypto.getRandomValues(new Uint8Array(8)));
|
|
| 368 | + |
|
| 369 | + // Convert hex-encoded password to an array of bytes.
|
|
| 370 | + const password = [];
|
|
| 371 | + for (let i = 0; i < aHexPassword.length; i += 2) {
|
|
| 372 | + password.push(parseInt(aHexPassword.substring(i, i + 2), 16));
|
|
| 373 | + }
|
|
| 374 | + |
|
| 375 | + // Run through the S2K algorithm and convert to a string.
|
|
| 376 | + const toHex = v => v.toString(16).padStart(2, "0");
|
|
| 377 | + const arrayToHex = aArray => aArray.map(toHex).join("");
|
|
| 378 | + const kCodedCount = 96;
|
|
| 379 | + const hashVal = this.#cryptoSecretToKey(password, salt, kCodedCount);
|
|
| 380 | + return "16:" + arrayToHex(salt) + toHex(kCodedCount) + arrayToHex(hashVal);
|
|
| 326 | 381 | }
|
| 327 | 382 | |
| 328 | - // Return a ControlPort or SocksPort argument for aIPCFile (an nsIFile).
|
|
| 329 | - // The result is unix:/path or unix:"/path with spaces" with appropriate
|
|
| 330 | - // C-style escaping within the path portion.
|
|
| 331 | - _ipcPortArg(aIPCFile) {
|
|
| 332 | - return "unix:" + TorParsers.escapeString(aIPCFile.path);
|
|
| 383 | + // #cryptoSecretToKey() is similar to Vidalia's crypto_secret_to_key().
|
|
| 384 | + // It generates and returns a hash of aPassword by following the iterated
|
|
| 385 | + // and salted S2K algorithm (see RFC 2440 section 3.6.1.3).
|
|
| 386 | + // See also https://gitlab.torproject.org/tpo/core/torspec/-/blob/main/control-spec.txt#L3824.
|
|
| 387 | + // Returns an array of bytes.
|
|
| 388 | + #cryptoSecretToKey(aPassword, aSalt, aCodedCount) {
|
|
| 389 | + const inputArray = aSalt.concat(aPassword);
|
|
| 390 | + |
|
| 391 | + // Subtle crypto only has the final digest, and does not allow incremental
|
|
| 392 | + // updates.
|
|
| 393 | + const hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
|
|
| 394 | + Ci.nsICryptoHash
|
|
| 395 | + );
|
|
| 396 | + hasher.init(hasher.SHA1);
|
|
| 397 | + const kEXPBIAS = 6;
|
|
| 398 | + let count = (16 + (aCodedCount & 15)) << ((aCodedCount >> 4) + kEXPBIAS);
|
|
| 399 | + while (count > 0) {
|
|
| 400 | + if (count > inputArray.length) {
|
|
| 401 | + hasher.update(inputArray, inputArray.length);
|
|
| 402 | + count -= inputArray.length;
|
|
| 403 | + } else {
|
|
| 404 | + const finalArray = inputArray.slice(0, count);
|
|
| 405 | + hasher.update(finalArray, finalArray.length);
|
|
| 406 | + count = 0;
|
|
| 407 | + }
|
|
| 408 | + }
|
|
| 409 | + return hasher
|
|
| 410 | + .finish(false)
|
|
| 411 | + .split("")
|
|
| 412 | + .map(b => b.charCodeAt(0));
|
|
| 333 | 413 | }
|
| 334 | 414 | } |
| ... | ... | @@ -289,9 +289,8 @@ export const TorProtocolService = { |
| 289 | 289 | // are also used in torbutton.
|
| 290 | 290 | |
| 291 | 291 | // Returns Tor password string or null if an error occurs.
|
| 292 | - torGetPassword(aPleaseHash) {
|
|
| 293 | - const pw = this._controlPassword;
|
|
| 294 | - return aPleaseHash ? this._hashPassword(pw) : pw;
|
|
| 292 | + torGetPassword() {
|
|
| 293 | + return this._controlPassword;
|
|
| 295 | 294 | },
|
| 296 | 295 | |
| 297 | 296 | torGetControlIPCFile() {
|
| ... | ... | @@ -306,6 +305,24 @@ export const TorProtocolService = { |
| 306 | 305 | return this._SOCKSPortInfo;
|
| 307 | 306 | },
|
| 308 | 307 | |
| 308 | + get torControlPortInfo() {
|
|
| 309 | + const info = {
|
|
| 310 | + password: this._controlPassword,
|
|
| 311 | + };
|
|
| 312 | + if (this._controlIPCFile) {
|
|
| 313 | + info.ipcFile = this._controlIPCFile?.clone();
|
|
| 314 | + }
|
|
| 315 | + if (this._controlPort) {
|
|
| 316 | + info.host = this._controlHost;
|
|
| 317 | + info.port = this._controlPort;
|
|
| 318 | + }
|
|
| 319 | + return info;
|
|
| 320 | + },
|
|
| 321 | + |
|
| 322 | + get torSOCKSPortInfo() {
|
|
| 323 | + return this._SOCKSPortInfo;
|
|
| 324 | + },
|
|
| 325 | + |
|
| 309 | 326 | // Public, but called only internally
|
| 310 | 327 | |
| 311 | 328 | // Executes a command on the control port.
|
| ... | ... | @@ -469,115 +486,8 @@ export const TorProtocolService = { |
| 469 | 486 | this._controlPassword = this._generateRandomPassword();
|
| 470 | 487 | }
|
| 471 | 488 | |
| 472 | - // Determine what kind of SOCKS port Tor and the browser will use.
|
|
| 473 | - // On Windows (where Unix domain sockets are not supported), TCP is
|
|
| 474 | - // always used.
|
|
| 475 | - //
|
|
| 476 | - // The following environment variables are supported and take
|
|
| 477 | - // precedence over preferences:
|
|
| 478 | - // TOR_SOCKS_IPC_PATH (file system path; ignored on Windows)
|
|
| 479 | - // TOR_SOCKS_HOST
|
|
| 480 | - // TOR_SOCKS_PORT
|
|
| 481 | - //
|
|
| 482 | - // The following preferences are consulted:
|
|
| 483 | - // network.proxy.socks
|
|
| 484 | - // network.proxy.socks_port
|
|
| 485 | - // extensions.torlauncher.socks_port_use_ipc (Boolean)
|
|
| 486 | - // extensions.torlauncher.socks_ipc_path (file system path)
|
|
| 487 | - // If extensions.torlauncher.socks_ipc_path is empty, a default
|
|
| 488 | - // path is used (<tor-data-directory>/socks.socket).
|
|
| 489 | - //
|
|
| 490 | - // When using TCP, if a value is not defined via an env variable it is
|
|
| 491 | - // taken from the corresponding browser preference if possible. The
|
|
| 492 | - // exceptions are:
|
|
| 493 | - // If network.proxy.socks contains a file: URL, a default value of
|
|
| 494 | - // "127.0.0.1" is used instead.
|
|
| 495 | - // If the network.proxy.socks_port value is 0, a default value of
|
|
| 496 | - // 9150 is used instead.
|
|
| 497 | - //
|
|
| 498 | - // Supported scenarios:
|
|
| 499 | - // 1. By default, an IPC object at a default path is used.
|
|
| 500 | - // 2. If extensions.torlauncher.socks_port_use_ipc is set to false,
|
|
| 501 | - // a TCP socket at 127.0.0.1:9150 is used, unless different values
|
|
| 502 | - // are set in network.proxy.socks and network.proxy.socks_port.
|
|
| 503 | - // 3. If the TOR_SOCKS_IPC_PATH env var is set, an IPC object at that
|
|
| 504 | - // path is used (e.g., a Unix domain socket).
|
|
| 505 | - // 4. If the TOR_SOCKS_HOST and/or TOR_SOCKS_PORT env vars are set, TCP
|
|
| 506 | - // is used. Values not set via env vars will be taken from the
|
|
| 507 | - // network.proxy.socks and network.proxy.socks_port prefs as described
|
|
| 508 | - // above.
|
|
| 509 | - // 5. If extensions.torlauncher.socks_port_use_ipc is true and
|
|
| 510 | - // extensions.torlauncher.socks_ipc_path is set, an IPC object at
|
|
| 511 | - // the specified path is used.
|
|
| 512 | - // 6. Tor Launcher is disabled. Torbutton will respect the env vars if
|
|
| 513 | - // present; if not, the values in network.proxy.socks and
|
|
| 514 | - // network.proxy.socks_port are used without modification.
|
|
| 515 | - |
|
| 516 | - let useIPC;
|
|
| 517 | - this._SOCKSPortInfo = { ipcFile: undefined, host: undefined, port: 0 };
|
|
| 518 | - if (!isWindows && Services.env.exists("TOR_SOCKS_IPC_PATH")) {
|
|
| 519 | - let ipcPath = Services.env.get("TOR_SOCKS_IPC_PATH");
|
|
| 520 | - this._SOCKSPortInfo.ipcFile = new lazy.FileUtils.File(ipcPath);
|
|
| 521 | - useIPC = true;
|
|
| 522 | - } else {
|
|
| 523 | - // Check for TCP host and port environment variables.
|
|
| 524 | - if (Services.env.exists("TOR_SOCKS_HOST")) {
|
|
| 525 | - this._SOCKSPortInfo.host = Services.env.get("TOR_SOCKS_HOST");
|
|
| 526 | - useIPC = false;
|
|
| 527 | - }
|
|
| 528 | - if (Services.env.exists("TOR_SOCKS_PORT")) {
|
|
| 529 | - this._SOCKSPortInfo.port = parseInt(
|
|
| 530 | - Services.env.get("TOR_SOCKS_PORT"),
|
|
| 531 | - 10
|
|
| 532 | - );
|
|
| 533 | - useIPC = false;
|
|
| 534 | - }
|
|
| 535 | - }
|
|
| 536 | - |
|
| 537 | - if (useIPC === undefined) {
|
|
| 538 | - useIPC =
|
|
| 539 | - !isWindows &&
|
|
| 540 | - Services.prefs.getBoolPref(
|
|
| 541 | - "extensions.torlauncher.socks_port_use_ipc",
|
|
| 542 | - false
|
|
| 543 | - );
|
|
| 544 | - }
|
|
| 545 | - |
|
| 546 | - // Fill in missing SOCKS info from prefs.
|
|
| 547 | - if (useIPC) {
|
|
| 548 | - if (!this._SOCKSPortInfo.ipcFile) {
|
|
| 549 | - this._SOCKSPortInfo.ipcFile = TorLauncherUtil.getTorFile(
|
|
| 550 | - "socks_ipc",
|
|
| 551 | - false
|
|
| 552 | - );
|
|
| 553 | - }
|
|
| 554 | - } else {
|
|
| 555 | - if (!this._SOCKSPortInfo.host) {
|
|
| 556 | - let socksAddr = Services.prefs.getCharPref(
|
|
| 557 | - "network.proxy.socks",
|
|
| 558 | - "127.0.0.1"
|
|
| 559 | - );
|
|
| 560 | - let socksAddrHasHost = socksAddr && !socksAddr.startsWith("file:");
|
|
| 561 | - this._SOCKSPortInfo.host = socksAddrHasHost ? socksAddr : "127.0.0.1";
|
|
| 562 | - }
|
|
| 563 | - |
|
| 564 | - if (!this._SOCKSPortInfo.port) {
|
|
| 565 | - let socksPort = Services.prefs.getIntPref(
|
|
| 566 | - "network.proxy.socks_port",
|
|
| 567 | - 0
|
|
| 568 | - );
|
|
| 569 | - // This pref is set as 0 by default in Firefox, use 9150 if we get 0.
|
|
| 570 | - this._SOCKSPortInfo.port = socksPort != 0 ? socksPort : 9150;
|
|
| 571 | - }
|
|
| 572 | - }
|
|
| 573 | - |
|
| 574 | - logger.info("SOCKS port type: " + (useIPC ? "IPC" : "TCP"));
|
|
| 575 | - if (useIPC) {
|
|
| 576 | - logger.info(`ipcFile: ${this._SOCKSPortInfo.ipcFile.path}`);
|
|
| 577 | - } else {
|
|
| 578 | - logger.info(`SOCKS host: ${this._SOCKSPortInfo.host}`);
|
|
| 579 | - logger.info(`SOCKS port: ${this._SOCKSPortInfo.port}`);
|
|
| 580 | - }
|
|
| 489 | + this._SOCKSPortInfo = TorLauncherUtil.getPreferredSocksConfiguration();
|
|
| 490 | + TorLauncherUtil.setProxyConfiguration(this._SOCKSPortInfo);
|
|
| 581 | 491 | |
| 582 | 492 | // Set the global control port info parameters.
|
| 583 | 493 | // These values may be overwritten by torbutton when it initializes, but
|
| ... | ... | @@ -781,38 +691,6 @@ export const TorProtocolService = { |
| 781 | 691 | return pwd;
|
| 782 | 692 | },
|
| 783 | 693 | |
| 784 | - // Based on Vidalia's TorSettings::hashPassword().
|
|
| 785 | - _hashPassword(aHexPassword) {
|
|
| 786 | - if (!aHexPassword) {
|
|
| 787 | - return null;
|
|
| 788 | - }
|
|
| 789 | - |
|
| 790 | - // Generate a random, 8 byte salt value.
|
|
| 791 | - const salt = Array.from(crypto.getRandomValues(new Uint8Array(8)));
|
|
| 792 | - |
|
| 793 | - // Convert hex-encoded password to an array of bytes.
|
|
| 794 | - const password = [];
|
|
| 795 | - for (let i = 0; i < aHexPassword.length; i += 2) {
|
|
| 796 | - password.push(parseInt(aHexPassword.substring(i, i + 2), 16));
|
|
| 797 | - }
|
|
| 798 | - |
|
| 799 | - // Run through the S2K algorithm and convert to a string.
|
|
| 800 | - const kCodedCount = 96;
|
|
| 801 | - const hashVal = this._cryptoSecretToKey(password, salt, kCodedCount);
|
|
| 802 | - if (!hashVal) {
|
|
| 803 | - logger.error("_cryptoSecretToKey() failed");
|
|
| 804 | - return null;
|
|
| 805 | - }
|
|
| 806 | - |
|
| 807 | - const arrayToHex = aArray =>
|
|
| 808 | - aArray.map(item => this._toHex(item, 2)).join("");
|
|
| 809 | - let rv = "16:";
|
|
| 810 | - rv += arrayToHex(salt);
|
|
| 811 | - rv += this._toHex(kCodedCount, 2);
|
|
| 812 | - rv += arrayToHex(hashVal);
|
|
| 813 | - return rv;
|
|
| 814 | - },
|
|
| 815 | - |
|
| 816 | 694 | // Returns -1 upon failure.
|
| 817 | 695 | _cryptoRandInt(aMax) {
|
| 818 | 696 | // Based on tor's crypto_rand_int().
|
| ... | ... | @@ -831,43 +709,6 @@ export const TorProtocolService = { |
| 831 | 709 | return val % aMax;
|
| 832 | 710 | },
|
| 833 | 711 | |
| 834 | - // _cryptoSecretToKey() is similar to Vidalia's crypto_secret_to_key().
|
|
| 835 | - // It generates and returns a hash of aPassword by following the iterated
|
|
| 836 | - // and salted S2K algorithm (see RFC 2440 section 3.6.1.3).
|
|
| 837 | - // Returns an array of bytes.
|
|
| 838 | - _cryptoSecretToKey(aPassword, aSalt, aCodedCount) {
|
|
| 839 | - if (!aPassword || !aSalt) {
|
|
| 840 | - return null;
|
|
| 841 | - }
|
|
| 842 | - |
|
| 843 | - const inputArray = aSalt.concat(aPassword);
|
|
| 844 | - |
|
| 845 | - // Subtle crypto only has the final digest, and does not allow incremental
|
|
| 846 | - // updates. Also, it is async, so we should hash and keep the hash in a
|
|
| 847 | - // variable if we wanted to switch to getters.
|
|
| 848 | - // So, keeping this implementation should be okay for now.
|
|
| 849 | - const hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
|
|
| 850 | - Ci.nsICryptoHash
|
|
| 851 | - );
|
|
| 852 | - hasher.init(hasher.SHA1);
|
|
| 853 | - const kEXPBIAS = 6;
|
|
| 854 | - let count = (16 + (aCodedCount & 15)) << ((aCodedCount >> 4) + kEXPBIAS);
|
|
| 855 | - while (count > 0) {
|
|
| 856 | - if (count > inputArray.length) {
|
|
| 857 | - hasher.update(inputArray, inputArray.length);
|
|
| 858 | - count -= inputArray.length;
|
|
| 859 | - } else {
|
|
| 860 | - const finalArray = inputArray.slice(0, count);
|
|
| 861 | - hasher.update(finalArray, finalArray.length);
|
|
| 862 | - count = 0;
|
|
| 863 | - }
|
|
| 864 | - }
|
|
| 865 | - return hasher
|
|
| 866 | - .finish(false)
|
|
| 867 | - .split("")
|
|
| 868 | - .map(b => b.charCodeAt(0));
|
|
| 869 | - },
|
|
| 870 | - |
|
| 871 | 712 | _toHex(aValue, aMinLen) {
|
| 872 | 713 | return aValue.toString(16).padStart(aMinLen, "0");
|
| 873 | 714 | },
|
| ... | ... | @@ -60,7 +60,7 @@ var torbutton_init; |
| 60 | 60 | } else {
|
| 61 | 61 | try {
|
| 62 | 62 | // Try to get password from Tor Launcher.
|
| 63 | - m_tb_control_pass = TorProtocolService.torGetPassword(false);
|
|
| 63 | + m_tb_control_pass = TorProtocolService.torGetPassword();
|
|
| 64 | 64 | } catch (e) {}
|
| 65 | 65 | }
|
| 66 | 66 |
| 1 | 1 | Classes = [
|
| 2 | - {
|
|
| 3 | - "cid": "{06322def-6fde-4c06-aef6-47ae8e799629}",
|
|
| 4 | - "contract_ids": [
|
|
| 5 | - "@torproject.org/startup-observer;1"
|
|
| 6 | - ],
|
|
| 7 | - "jsm": "resource://torbutton/modules/TorbuttonStartupObserver.jsm",
|
|
| 8 | - "constructor": "StartupObserver",
|
|
| 9 | - },
|
|
| 10 | 2 | {
|
| 11 | 3 | "cid": "{f36d72c9-9718-4134-b550-e109638331d7}",
|
| 12 | 4 | "contract_ids": [
|
| 1 | -// Bug 1506 P1-3: This code is mostly hackish remnants of session store
|
|
| 2 | -// support. There are a couple of observer events that *might* be worth
|
|
| 3 | -// listening to. Search for 1506 in the code.
|
|
| 4 | - |
|
| 5 | -/*************************************************************************
|
|
| 6 | - * Startup observer (JavaScript XPCOM component)
|
|
| 7 | - *
|
|
| 8 | - * Cases tested (each during Tor and Non-Tor, FF4 and FF3.6)
|
|
| 9 | - * 1. Crash
|
|
| 10 | - * 2. Upgrade
|
|
| 11 | - * 3. Fresh install
|
|
| 12 | - *
|
|
| 13 | - *************************************************************************/
|
|
| 14 | - |
|
| 15 | -var EXPORTED_SYMBOLS = ["StartupObserver"];
|
|
| 16 | - |
|
| 17 | -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
| 18 | -const { XPCOMUtils } = ChromeUtils.import(
|
|
| 19 | - "resource://gre/modules/XPCOMUtils.jsm"
|
|
| 20 | -);
|
|
| 21 | - |
|
| 22 | -const { TorProtocolService } = ChromeUtils.import(
|
|
| 23 | - "resource://gre/modules/TorProtocolService.jsm"
|
|
| 24 | -);
|
|
| 25 | - |
|
| 26 | -const lazy = {};
|
|
| 27 | - |
|
| 28 | -XPCOMUtils.defineLazyModuleGetters(lazy, {
|
|
| 29 | - FileUtils: "resource://gre/modules/FileUtils.jsm",
|
|
| 30 | -});
|
|
| 31 | - |
|
| 32 | -function StartupObserver() {
|
|
| 33 | - this.logger = Cc["@torproject.org/torbutton-logger;1"].getService(
|
|
| 34 | - Ci.nsISupports
|
|
| 35 | - ).wrappedJSObject;
|
|
| 36 | - this._prefs = Services.prefs;
|
|
| 37 | - this.logger.log(3, "Startup Observer created");
|
|
| 38 | - |
|
| 39 | - try {
|
|
| 40 | - // XXX: We're in a race with HTTPS-Everywhere to update our proxy settings
|
|
| 41 | - // before the initial SSL-Observatory test... If we lose the race, Firefox
|
|
| 42 | - // caches the old proxy settings for check.tp.o somehwere, and it never loads :(
|
|
| 43 | - this.setProxySettings();
|
|
| 44 | - } catch (e) {
|
|
| 45 | - this.logger.log(
|
|
| 46 | - 4,
|
|
| 47 | - "Early proxy change failed. Will try again at profile load. Error: " + e
|
|
| 48 | - );
|
|
| 49 | - }
|
|
| 50 | -}
|
|
| 51 | - |
|
| 52 | -StartupObserver.prototype = {
|
|
| 53 | - // Bug 6803: We need to get the env vars early due to
|
|
| 54 | - // some weird proxy caching code that showed up in FF15.
|
|
| 55 | - // Otherwise, homepage domain loads fail forever.
|
|
| 56 | - setProxySettings() {
|
|
| 57 | - // Bug 1506: Still want to get these env vars
|
|
| 58 | - if (Services.env.exists("TOR_TRANSPROXY")) {
|
|
| 59 | - this.logger.log(3, "Resetting Tor settings to transproxy");
|
|
| 60 | - this._prefs.setBoolPref("network.proxy.socks_remote_dns", false);
|
|
| 61 | - this._prefs.setIntPref("network.proxy.type", 0);
|
|
| 62 | - this._prefs.setIntPref("network.proxy.socks_port", 0);
|
|
| 63 | - this._prefs.setCharPref("network.proxy.socks", "");
|
|
| 64 | - } else {
|
|
| 65 | - // Try to retrieve SOCKS proxy settings from Tor Launcher.
|
|
| 66 | - let socksPortInfo;
|
|
| 67 | - try {
|
|
| 68 | - socksPortInfo = TorProtocolService.torGetSOCKSPortInfo();
|
|
| 69 | - } catch (e) {
|
|
| 70 | - this.logger.log(3, "tor launcher failed " + e);
|
|
| 71 | - }
|
|
| 72 | - |
|
| 73 | - // If Tor Launcher is not available, check environment variables.
|
|
| 74 | - if (!socksPortInfo) {
|
|
| 75 | - socksPortInfo = { ipcFile: undefined, host: undefined, port: 0 };
|
|
| 76 | - |
|
| 77 | - let isWindows = Services.appinfo.OS === "WINNT";
|
|
| 78 | - if (!isWindows && Services.env.exists("TOR_SOCKS_IPC_PATH")) {
|
|
| 79 | - socksPortInfo.ipcFile = new lazy.FileUtils.File(
|
|
| 80 | - Services.env.get("TOR_SOCKS_IPC_PATH")
|
|
| 81 | - );
|
|
| 82 | - } else {
|
|
| 83 | - if (Services.env.exists("TOR_SOCKS_HOST")) {
|
|
| 84 | - socksPortInfo.host = Services.env.get("TOR_SOCKS_HOST");
|
|
| 85 | - }
|
|
| 86 | - if (Services.env.exists("TOR_SOCKS_PORT")) {
|
|
| 87 | - socksPortInfo.port = parseInt(Services.env.get("TOR_SOCKS_PORT"));
|
|
| 88 | - }
|
|
| 89 | - }
|
|
| 90 | - }
|
|
| 91 | - |
|
| 92 | - // Adjust network.proxy prefs.
|
|
| 93 | - if (socksPortInfo.ipcFile) {
|
|
| 94 | - let fph = Services.io
|
|
| 95 | - .getProtocolHandler("file")
|
|
| 96 | - .QueryInterface(Ci.nsIFileProtocolHandler);
|
|
| 97 | - let fileURI = fph.newFileURI(socksPortInfo.ipcFile);
|
|
| 98 | - this.logger.log(3, "Reset socks to " + fileURI.spec);
|
|
| 99 | - this._prefs.setCharPref("network.proxy.socks", fileURI.spec);
|
|
| 100 | - this._prefs.setIntPref("network.proxy.socks_port", 0);
|
|
| 101 | - } else {
|
|
| 102 | - if (socksPortInfo.host) {
|
|
| 103 | - this._prefs.setCharPref("network.proxy.socks", socksPortInfo.host);
|
|
| 104 | - this.logger.log(3, "Reset socks host to " + socksPortInfo.host);
|
|
| 105 | - }
|
|
| 106 | - if (socksPortInfo.port) {
|
|
| 107 | - this._prefs.setIntPref(
|
|
| 108 | - "network.proxy.socks_port",
|
|
| 109 | - socksPortInfo.port
|
|
| 110 | - );
|
|
| 111 | - this.logger.log(3, "Reset socks port to " + socksPortInfo.port);
|
|
| 112 | - }
|
|
| 113 | - }
|
|
| 114 | - |
|
| 115 | - if (socksPortInfo.ipcFile || socksPortInfo.host || socksPortInfo.port) {
|
|
| 116 | - this._prefs.setBoolPref("network.proxy.socks_remote_dns", true);
|
|
| 117 | - this._prefs.setIntPref("network.proxy.type", 1);
|
|
| 118 | - }
|
|
| 119 | - }
|
|
| 120 | - |
|
| 121 | - // Force prefs to be synced to disk
|
|
| 122 | - Services.prefs.savePrefFile(null);
|
|
| 123 | - |
|
| 124 | - this.logger.log(3, "Synced network settings to environment.");
|
|
| 125 | - },
|
|
| 126 | - |
|
| 127 | - observe(subject, topic, data) {
|
|
| 128 | - if (topic == "profile-after-change") {
|
|
| 129 | - this.setProxySettings();
|
|
| 130 | - }
|
|
| 131 | - |
|
| 132 | - // In all cases, force prefs to be synced to disk
|
|
| 133 | - Services.prefs.savePrefFile(null);
|
|
| 134 | - },
|
|
| 135 | - |
|
| 136 | - // Hack to get us registered early to observe recovery
|
|
| 137 | - _xpcom_categories: [{ category: "profile-after-change" }],
|
|
| 138 | -}; |
| ... | ... | @@ -8,7 +8,3 @@ JAR_MANIFESTS += ['jar.mn'] |
| 8 | 8 | XPCOM_MANIFESTS += [
|
| 9 | 9 | "components.conf",
|
| 10 | 10 | ] |
| 11 | - |
|
| 12 | -EXTRA_COMPONENTS += [
|
|
| 13 | - "torbutton.manifest",
|
|
| 14 | -] |
| 1 | -category profile-after-change StartupObserver @torproject.org/startup-observer;1 |