Verified Commit f803ad03 authored by Pier Angelo Vendrame's avatar Pier Angelo Vendrame 🎃
Browse files

fixup! Bug 40933: Add tor-launcher functionality

Use a custom TorProcessAndroid in the TorProvider on Android.
parent e2537bc3
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -5,6 +5,10 @@

var EXPORTED_SYMBOLS = ["GeckoViewStartup"];

const { AppConstants } = ChromeUtils.importESModule(
  "resource://gre/modules/AppConstants.sys.mjs"
);

const { GeckoViewUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/GeckoViewUtils.sys.mjs"
);
@@ -17,6 +21,9 @@ ChromeUtils.defineESModuleGetters(lazy, {
  PdfJs: "resource://pdf.js/PdfJs.sys.mjs",
  Preferences: "resource://gre/modules/Preferences.sys.mjs",
  RFPHelper: "resource://gre/modules/RFPHelper.sys.mjs",
  TorConnect: "resource://gre/modules/TorConnect.sys.mjs",
  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
  TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
});

const { XPCOMUtils } = ChromeUtils.importESModule(
@@ -258,6 +265,18 @@ class GeckoViewStartup {
          "GeckoView:SetLocale",
        ]);

        if (
          AppConstants.MOZ_UPDATE_CHANNEL !== "release" &&
          AppConstants.MOZ_UPDATE_CHANNEL !== "alpha"
        ) {
          lazy.TorProviderBuilder.init().finally(() => {
            lazy.TorProviderBuilder.firstWindowLoaded();
          });
          lazy.TorSettings.init().then(() => {
            lazy.TorConnect.init();
          });
        }

        Services.obs.addObserver(this, "browser-idle-startup-tasks-finished");
        Services.obs.addObserver(this, "handlersvc-store-initialized");

+115 −0
Original line number Diff line number Diff line
/* 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 { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";

const lazy = {};

ChromeUtils.defineESModuleGetters(lazy, {
  EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
});

// The only event we might emit
const TOR_START_EVENT = "GeckoView:Tor:StartTor";

const logger = new ConsoleAPI({
  maxLogLevel: "info",
  prefix: "TorProcessAndroid",
});

// The events we will listen to
const TorEvents = Object.freeze({
  started: "GeckoView:Tor:TorStarted",
  startFailed: "GeckoView:Tor:TorStartFailed",
  exited: "GeckoView:Tor:TorExited",
});

export class TorProcessAndroid {
  /**
   * The handle the Java counterpart uses to refer to the process we started.
   * We use it to filter the exit events and make sure they refer to the daemon
   * we are interested in.
   */
  #processHandle = null;
  /**
   * The promise resolver we call when the Java counterpart sends the event that
   * tor has started.
   */
  #startResolve = null;
  /**
   * The promise resolver we call when the Java counterpart sends the event that
   * it failed to start tor.
   */
  #startReject = null;

  onExit = () => {};

  get isRunning() {
    return !!this.#processHandle;
  }

  async start() {
    // Generate the handle on the JS side so that it's ready in case it takes
    // less to start the process than to propagate the success.
    this.#processHandle = crypto.randomUUID();
    logger.info(`Starting new process with handle ${this.#processHandle}`);
    // Let's declare it immediately, so that the Java side can do its stuff in
    // an async manner and we avoid possible race conditions (at most we await
    // an already resolved/rejected promise.
    const startEventPromise = new Promise((resolve, reject) => {
      this.#startResolve = resolve;
      this.#startReject = reject;
    });
    lazy.EventDispatcher.instance.registerListener(
      this,
      Object.values(TorEvents)
    );
    let config;
    try {
      config = await lazy.EventDispatcher.instance.sendRequestForResult({
        type: TOR_START_EVENT,
        handle: this.#processHandle,
      });
      logger.debug("Sent the start event.");
    } catch (e) {
      this.forget();
      throw e;
    }
    await startEventPromise;
    return config;
  }

  forget() {
    // Processes usually exit when we close the control port connection to them.
    logger.trace(`Forgetting process ${this.#processHandle}`);
    this.#processHandle = null;
    lazy.EventDispatcher.instance.unregisterListener(
      this,
      Object.values(TorEvents)
    );
  }

  onEvent(event, data, callback) {
    if (data?.handle !== this.#processHandle) {
      logger.debug(`Ignoring event ${event} with another handle`, data);
      return;
    }
    logger.info(`Received an event ${event}`, data);
    switch (event) {
      case TorEvents.started:
        this.#startResolve();
        break;
      case TorEvents.startFailed:
        this.#startReject(new Error(data.error));
        break;
      case TorEvents.exited:
        this.forget();
        if (this.#startReject !== null) {
          this.#startReject();
        }
        this.onExit(data.status);
        break;
    }
  }
}
+31 −7
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
  FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
  TorController: "resource://gre/modules/TorControlPort.sys.mjs",
  TorProcess: "resource://gre/modules/TorProcess.sys.mjs",
  TorProcessAndroid: "resource://gre/modules/TorProcessAndroid.sys.mjs",
});

const logger = new ConsoleAPI({
@@ -182,8 +183,12 @@ export class TorProvider {
    logger.debug("Initializing the Tor provider.");

    // These settings might be customized in the following steps.
    if (TorLauncherUtil.isAndroid) {
      this.#socksSettings = { transproxy: false };
    } else {
      this.#socksSettings = TorLauncherUtil.getPreferredSocksConfiguration();
      logger.debug("Requested SOCKS configuration", this.#socksSettings);
    }

    try {
      await this.#setControlPortConfiguration();
@@ -490,10 +495,14 @@ export class TorProvider {
      return;
    }

    if (TorLauncherUtil.isAndroid) {
      this.#torProcess = new lazy.TorProcessAndroid();
    } else {
      this.#torProcess = new lazy.TorProcess(
        this.#controlPortSettings,
        this.#socksSettings
      );
    }
    // Use a closure instead of bind because we reassign #cancelConnection.
    // Also, we now assign an exit handler that cancels the first connection,
    // so that a sudden exit before the first connection is completed might
@@ -507,7 +516,17 @@ export class TorProvider {
    };

    logger.debug("Trying to start the tor process.");
    await this.#torProcess.start();
    const res = await this.#torProcess.start();
    if (TorLauncherUtil.isAndroid) {
      this.#controlPortSettings = {
        ipcFile: new lazy.FileUtils.File(res.controlPortPath),
        cookieFilePath: res.cookieFilePath,
      };
      this.#socksSettings = {
        transproxy: false,
        ipcFile: new lazy.FileUtils.File(res.socksPath),
      };
    }
    logger.info("Started a tor process");
  }

@@ -521,6 +540,11 @@ export class TorProvider {
    logger.debug("Reading the control port configuration");
    const settings = {};

    if (TorLauncherUtil.isAndroid) {
      // We will populate the settings after having started the daemon.
      return;
    }

    const isWindows = Services.appinfo.OS === "WINNT";
    // Determine how Tor Launcher will connect to the Tor control port.
    // Environment variables get top priority followed by preferences.
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ EXTRA_JS_MODULES += [
    "TorLauncherUtil.sys.mjs",
    "TorParsers.sys.mjs",
    "TorProcess.sys.mjs",
    "TorProcessAndroid.sys.mjs",
    "TorProvider.sys.mjs",
    "TorProviderBuilder.sys.mjs",
    "TorStartupService.sys.mjs",