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

fixup! Bug 40597: Implement TorSettings module

Bug 41114: Refactor TorConnect.

Move some parts of BootstrappingState.run to methods on their own, to
make it easier to understand this function.
parent d214f39b
Loading
Loading
Loading
Loading
+91 −77
Changes for toolkit/modules/TorConnect.sys.mjs: 91 added lines, 77 removed lines.
Original line number Diff line number Diff line
@@ -297,6 +297,12 @@ class ConfiguringState extends StateCallback {
}

class BootstrappingState extends StateCallback {
  #bootstrap = null;
  #bootstrapError = "";
  #bootstrapErrorDetails = "";
  #internetTest = null;
  #cancelled = false;

  allowedTransitions = Object.freeze([
    TorConnectState.Configuring,
    TorConnectState.Bootstrapped,
@@ -308,50 +314,78 @@ class BootstrappingState extends StateCallback {
  }

  async run() {
    // debug hook to simulate censorship preventing bootstrapping
    if (Services.prefs.getIntPref(TorConnectPrefs.censorship_level, 0) > 0) {
      await debugSleep(1500);
      TorConnect._hasBootstrapEverFailed = true;
      if (
        Services.prefs.getIntPref(TorConnectPrefs.censorship_level, 0) === 2
      ) {
        const codes = Object.keys(TorConnect._countryNames);
        TorConnect._detectedLocation =
          codes[Math.floor(Math.random() * codes.length)];
    if (await this.#simulateCensorship()) {
      return;
    }
      TorConnect._changeState(
        TorConnectState.Error,
        "Bootstrap failed (for debugging purposes)",
        "Error: Censorship simulation",
        true
      );

    this.#bootstrap = new lazy.TorBootstrapRequest();
    this.#bootstrap.onbootstrapstatus = (progress, status) => {
      TorConnect._updateBootstrapStatus(progress, status);
    };
    this.#bootstrap.onbootstrapcomplete = () => {
      this.#internetTest.cancel();
      this.changeState(TorConnectState.Bootstrapped);
    };
    this.#bootstrap.onbootstraperror = (message, details) => {
      if (this.#cancelled) {
        // We ignore this error since it occurred after cancelling (by the
        // user). We assume the error is just a side effect of the cancelling.
        // E.g. If the cancelling is triggered late in the process, we get
        // "Building circuits: Establishing a Tor circuit failed".
        // TODO: Maybe move this logic deeper in the process to know when to
        // filter out such errors triggered by cancelling.
        lazy.logger.warn(`Post-cancel error => ${message}; ${details}`);
        return;
      }
      // We have to wait for the Internet test to finish before sending the
      // bootstrap error
      this.#bootstrapError = message;
      this.#bootstrapErrorDetails = details;
      this.#maybeTransitionToError();
    };

    const tbr = new lazy.TorBootstrapRequest();
    const internetTest = new InternetTest();
    let cancelled = false;
    this.#internetTest = new InternetTest();
    this.#internetTest.onResult = (status, date) => {
      // TODO: Use the date to save the clock skew?
      TorConnect._internetStatus = status;
      this.#maybeTransitionToError();
    };
    this.#internetTest.onError = () => {
      this.#maybeTransitionToError();
    };

    this.#bootstrap.bootstrap();
  }

    let bootstrapError = "";
    let bootstrapErrorDetails = "";
    const maybeTransitionToError = () => {
  async cleanup(nextState) {
    if (nextState === TorConnectState.Configuring) {
      // stop bootstrap process if user cancelled
      this.#cancelled = true;
      this.#internetTest?.cancel();
      await this.#bootstrap?.cancel();
    }
  }

  #maybeTransitionToError() {
    if (
        internetTest.status === InternetStatus.Unknown &&
        internetTest.error === null &&
        internetTest.enabled
      this.#internetTest.status === InternetStatus.Unknown &&
      this.#internetTest.error === null &&
      this.#internetTest.enabled
    ) {
        // We have been called by a failed bootstrap, but the internet test has not run yet - force
        // it to run immediately!
        internetTest.test();
        // Return from this call, because the Internet test's callback will call us again
      // We have been called by a failed bootstrap, but the internet test has
      // not run yet - force it to run immediately!
      this.#internetTest.test();
      // Return from this call, because the Internet test's callback will call
      // us again.
      return;
    }
      // Do not transition to the offline error until we are sure that also the bootstrap failed, in
      // case Moat is down but the bootstrap can proceed anyway.
      if (bootstrapError === "") {
    // Do not transition to the offline error until we are sure that also the
    // bootstrap failed, in case Moat is down but the bootstrap can proceed
    // anyway.
    if (this.#bootstrapError === "") {
      return;
    }
      if (internetTest.status === InternetStatus.Offline) {
    if (this.#internetTest.status === InternetStatus.Offline) {
      this.changeState(
        TorConnectState.Error,
        TorStrings.torConnect.offline,
@@ -363,57 +397,37 @@ class BootstrappingState extends StateCallback {
      TorConnect._hasBootstrapEverFailed = true;
      this.changeState(
        TorConnectState.Error,
          bootstrapError,
          bootstrapErrorDetails,
        this.#bootstrapError,
        this.#bootstrapErrorDetails,
        true
      );
    }
    };

    this.cleanup = async nextState => {
      if (nextState === TorConnectState.Configuring) {
        // stop bootstrap process if user cancelled
        cancelled = true;
        internetTest.cancel();
        await tbr.cancel();
  }
    };

    tbr.onbootstrapstatus = (progress, status) => {
      TorConnect._updateBootstrapStatus(progress, status);
    };
    tbr.onbootstrapcomplete = () => {
      internetTest.cancel();
      this.changeState(TorConnectState.Bootstrapped);
    };
    tbr.onbootstraperror = (message, details) => {
      if (cancelled) {
        // We ignore this error since it occurred after cancelling (by
        // the user). We assume the error is just a side effect of the
        // cancelling.
        // E.g. If the cancelling is triggered late in the process, we
        // get "Building circuits: Establishing a Tor circuit failed".
        // TODO: Maybe move this logic deeper in the process to know
        // when to filter out such errors triggered by cancelling.
        lazy.logger.warn(`Post-cancel error => ${message}; ${details}`);
        return;
  async #simulateCensorship() {
    // debug hook to simulate censorship preventing bootstrapping
    const censorshipLevel = Services.prefs.getIntPref(
      TorConnectPrefs.censorship_level,
      0
    );
    if (censorshipLevel <= 0) {
      return false;
    }
      // We have to wait for the Internet test to finish before sending the bootstrap error
      bootstrapError = message;
      bootstrapErrorDetails = details;
      maybeTransitionToError();
    };

    internetTest.onResult = (status, date) => {
      // TODO: Use the date to save the clock skew?
      TorConnect._internetStatus = status;
      maybeTransitionToError();
    };
    internetTest.onError = () => {
      maybeTransitionToError();
    };

    tbr.bootstrap();
    await debugSleep(1500);
    TorConnect._hasBootstrapEverFailed = true;
    if (censorshipLevel === 2) {
      const codes = Object.keys(TorConnect._countryNames);
      TorConnect._detectedLocation =
        codes[Math.floor(Math.random() * codes.length)];
    }
    this.changeState(
      TorConnectState.Error,
      "Bootstrap failed (for debugging purposes)",
      "Error: Censorship simulation",
      true
    );
    return true;
  }
}