Pier Angelo Vendrame pushed to branch tor-browser-128.6.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
-
0b42b720
by Henry Wilkes at 2025-01-20T17:57:59+00:00
-
7129f408
by Henry Wilkes at 2025-01-20T17:57:59+00:00
-
6ea6bd74
by Henry Wilkes at 2025-01-20T17:57:59+00:00
4 changed files:
- toolkit/components/tor-launcher/TorProvider.sys.mjs
- toolkit/modules/Moat.sys.mjs
- toolkit/modules/TorConnect.sys.mjs
- toolkit/modules/TorSettings.sys.mjs
Changes:
| ... | ... | @@ -227,7 +227,7 @@ export class TorProvider { |
| 227 | 227 | if (this.ownsTorDaemon) {
|
| 228 | 228 | try {
|
| 229 | 229 | await lazy.TorSettings.initializedPromise;
|
| 230 | - await this.writeSettings(lazy.TorSettings.getSettings());
|
|
| 230 | + await this.writeSettings();
|
|
| 231 | 231 | } catch (e) {
|
| 232 | 232 | logger.warn(
|
| 233 | 233 | "Failed to initialize TorSettings or to write our initial settings. Continuing the initialization anyway.",
|
| ... | ... | @@ -269,11 +269,13 @@ export class TorProvider { |
| 269 | 269 | /**
|
| 270 | 270 | * Send settings to the tor daemon.
|
| 271 | 271 | *
|
| 272 | - * @param {object} settings A settings object, as returned by
|
|
| 273 | - * TorSettings.getSettings(). This allow to try settings without passing
|
|
| 274 | - * through TorSettings.
|
|
| 272 | + * This should only be called internally or by the TorSettings module.
|
|
| 275 | 273 | */
|
| 276 | - async writeSettings(settings) {
|
|
| 274 | + async writeSettings() {
|
|
| 275 | + // Fetch the current settings.
|
|
| 276 | + // We set the useTemporary parameter since we want to apply temporary
|
|
| 277 | + // bridges if they are available.
|
|
| 278 | + const settings = lazy.TorSettings.getSettings(true);
|
|
| 277 | 279 | logger.debug("TorProvider.writeSettings", settings);
|
| 278 | 280 | const torSettings = new Map();
|
| 279 | 281 |
| ... | ... | @@ -13,7 +13,6 @@ ChromeUtils.defineESModuleGetters(lazy, { |
| 13 | 13 | DomainFrontRequestBuilder:
|
| 14 | 14 | "resource://gre/modules/DomainFrontedRequests.sys.mjs",
|
| 15 | 15 | TorBridgeSource: "resource://gre/modules/TorSettings.sys.mjs",
|
| 16 | - TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
|
|
| 17 | 16 | });
|
| 18 | 17 | |
| 19 | 18 | const TorLauncherPrefs = Object.freeze({
|
| ... | ... | @@ -73,6 +72,25 @@ class InternetTestResponseListener { |
| 73 | 72 | }
|
| 74 | 73 | }
|
| 75 | 74 | |
| 75 | +/**
|
|
| 76 | + * @typedef {Object} MoatBridges
|
|
| 77 | + *
|
|
| 78 | + * Bridge settings that can be passed to TorSettings.bridges.
|
|
| 79 | + *
|
|
| 80 | + * @property {number} source - The `TorBridgeSource` type.
|
|
| 81 | + * @property {string} [builtin_type] - The built-in bridge type.
|
|
| 82 | + * @property {string[]} [bridge_strings] - The bridge lines.
|
|
| 83 | + */
|
|
| 84 | + |
|
| 85 | +/**
|
|
| 86 | + * @typedef {Object} MoatSettings
|
|
| 87 | + *
|
|
| 88 | + * The settings returned by Moat.
|
|
| 89 | + *
|
|
| 90 | + * @property {MoatBridges[]} bridgesList - The list of bridges found.
|
|
| 91 | + * @property {string} [country] - The detected country (region).
|
|
| 92 | + */
|
|
| 93 | + |
|
| 76 | 94 | /**
|
| 77 | 95 | * Constructs JSON objects and sends requests over Moat.
|
| 78 | 96 | * The documentation about the JSON schemas to use are available at
|
| ... | ... | @@ -213,37 +231,31 @@ export class MoatRPC { |
| 213 | 231 | return { bridges, qrcode: qrcodeImg };
|
| 214 | 232 | }
|
| 215 | 233 | |
| 216 | - // Convert received settings object to format used by TorSettings module.
|
|
| 217 | - #fixupSettings(settings) {
|
|
| 234 | + /**
|
|
| 235 | + * Extract bridges from the received Moat settings object.
|
|
| 236 | + *
|
|
| 237 | + * @param {Object} settings - The received settings.
|
|
| 238 | + * @return {MoatBridge} The extracted bridges.
|
|
| 239 | + */
|
|
| 240 | + #extractBridges(settings) {
|
|
| 218 | 241 | if (!("bridges" in settings)) {
|
| 219 | 242 | throw new Error("Expected to find `bridges` in the settings object.");
|
| 220 | 243 | }
|
| 221 | - const retval = {
|
|
| 222 | - bridges: {
|
|
| 223 | - enabled: true,
|
|
| 224 | - },
|
|
| 225 | - };
|
|
| 244 | + const bridges = {};
|
|
| 226 | 245 | switch (settings.bridges.source) {
|
| 227 | 246 | case "builtin":
|
| 228 | - retval.bridges.source = lazy.TorBridgeSource.BuiltIn;
|
|
| 229 | - retval.bridges.builtin_type = settings.bridges.type;
|
|
| 230 | - // TorSettings will ignore strings for built-in bridges, and use the
|
|
| 231 | - // ones it already knows, instead. However, when we try these settings
|
|
| 232 | - // in the connect assist, we skip TorSettings. Therefore, we set the
|
|
| 233 | - // lines also here (the ones we already know, not the ones we receive
|
|
| 234 | - // from Moat). This needs TorSettings to be initialized, which by now
|
|
| 235 | - // should have already happened (this method is used only by TorConnect,
|
|
| 236 | - // that needs TorSettings to be initialized).
|
|
| 237 | - // In any case, getBuiltinBridges will throw if the data is not ready,
|
|
| 238 | - // yet.
|
|
| 239 | - retval.bridges.bridge_strings = lazy.TorSettings.getBuiltinBridges(
|
|
| 240 | - settings.bridges.type
|
|
| 241 | - );
|
|
| 247 | + bridges.source = lazy.TorBridgeSource.BuiltIn;
|
|
| 248 | + bridges.builtin_type = String(settings.bridges.type);
|
|
| 249 | + // Ignore the bridge_strings argument since we will use our built-in
|
|
| 250 | + // bridge strings instead.
|
|
| 242 | 251 | break;
|
| 243 | 252 | case "bridgedb":
|
| 244 | - retval.bridges.source = lazy.TorBridgeSource.BridgeDB;
|
|
| 245 | - if (settings.bridges.bridge_strings) {
|
|
| 246 | - retval.bridges.bridge_strings = settings.bridges.bridge_strings;
|
|
| 253 | + bridges.source = lazy.TorBridgeSource.BridgeDB;
|
|
| 254 | + if (settings.bridges.bridge_strings?.length) {
|
|
| 255 | + bridges.bridge_strings = Array.from(
|
|
| 256 | + settings.bridges.bridge_strings,
|
|
| 257 | + item => String(item)
|
|
| 258 | + );
|
|
| 247 | 259 | } else {
|
| 248 | 260 | throw new Error(
|
| 249 | 261 | "Received no bridge-strings for BridgeDB bridge source"
|
| ... | ... | @@ -255,37 +267,38 @@ export class MoatRPC { |
| 255 | 267 | `Unexpected bridge source '${settings.bridges.source}'`
|
| 256 | 268 | );
|
| 257 | 269 | }
|
| 258 | - return retval;
|
|
| 270 | + return bridges;
|
|
| 259 | 271 | }
|
| 260 | 272 | |
| 261 | - // Converts a list of settings objects received from BridgeDB to a list of
|
|
| 262 | - // settings objects understood by the TorSettings module.
|
|
| 263 | - // In the event of error, returns an empty list.
|
|
| 264 | - #fixupSettingsList(settingsList) {
|
|
| 265 | - const retval = [];
|
|
| 273 | + /**
|
|
| 274 | + * Extract a list of bridges from the received Moat settings object.
|
|
| 275 | + *
|
|
| 276 | + * @param {Object} settings - The received settings.
|
|
| 277 | + * @return {MoatBridge[]} The list of extracted bridges.
|
|
| 278 | + */
|
|
| 279 | + #extractBridgesList(settingsList) {
|
|
| 280 | + const bridgesList = [];
|
|
| 266 | 281 | for (const settings of settingsList) {
|
| 267 | 282 | try {
|
| 268 | - retval.push(this.#fixupSettings(settings));
|
|
| 283 | + bridgesList.push(this.#extractBridges(settings));
|
|
| 269 | 284 | } catch (ex) {
|
| 270 | 285 | log.error(ex);
|
| 271 | 286 | }
|
| 272 | 287 | }
|
| 273 | - return retval;
|
|
| 288 | + return bridgesList;
|
|
| 274 | 289 | }
|
| 275 | 290 | |
| 276 | - // Request tor settings for the user optionally based on their location
|
|
| 277 | - // (derived from their IP). Takes the following parameters:
|
|
| 278 | - // - transports: optional, an array of transports available to the client; if
|
|
| 279 | - // empty (or not given) returns settings using all working transports known
|
|
| 280 | - // to the server
|
|
| 281 | - // - country: optional, an ISO 3166-1 alpha-2 country code to request settings
|
|
| 282 | - // for; if not provided the country is determined by the user's IP address
|
|
| 283 | - //
|
|
| 284 | - // Returns an object with the detected country code and an array of settings
|
|
| 285 | - // in a format that can be passed to the TorSettings module. This array might
|
|
| 286 | - // be empty if the country has no associated settings.
|
|
| 287 | - // If the server cannot determine the user's country (and no country code is
|
|
| 288 | - // provided), then null is returned instead of the object.
|
|
| 291 | + /**
|
|
| 292 | + * Request tor settings for the user optionally based on their location
|
|
| 293 | + * (derived from their IP). Takes the following parameters:
|
|
| 294 | + *
|
|
| 295 | + * @param {string[]} transports - A list of transports we support.
|
|
| 296 | + * @param {?string} country - The region to request bridges for, as an
|
|
| 297 | + * ISO 3166-1 alpha-2 region code, or `null` to have the server
|
|
| 298 | + * automatically determine the region.
|
|
| 299 | + * @returns {?MoatSettings} - The returned settings from the server, or `null`
|
|
| 300 | + * if the region could not be determined by the server.
|
|
| 301 | + */
|
|
| 289 | 302 | async circumvention_settings(transports, country) {
|
| 290 | 303 | const args = {
|
| 291 | 304 | transports: transports ? transports : [],
|
| ... | ... | @@ -306,7 +319,7 @@ export class MoatRPC { |
| 306 | 319 | |
| 307 | 320 | throw new Error(`MoatRPC: ${detail} (${code})`);
|
| 308 | 321 | } else if ("settings" in response) {
|
| 309 | - settings.settings = this.#fixupSettingsList(response.settings);
|
|
| 322 | + settings.bridgesList = this.#extractBridgesList(response.settings);
|
|
| 310 | 323 | }
|
| 311 | 324 | if ("country" in response) {
|
| 312 | 325 | settings.country = response.country;
|
| ... | ... | @@ -349,14 +362,12 @@ export class MoatRPC { |
| 349 | 362 | return map;
|
| 350 | 363 | }
|
| 351 | 364 | |
| 352 | - // Request a copy of the defaul/fallback bridge settings, takes the following
|
|
| 353 | - // parameters:
|
|
| 354 | - // - transports: optional, an array of transports available to the client; if
|
|
| 355 | - // empty (or not given) returns settings using all working transports known
|
|
| 356 | - // to the server
|
|
| 357 | - //
|
|
| 358 | - // returns an array of settings objects in roughly the same format as the
|
|
| 359 | - // _settings object on the TorSettings module
|
|
| 365 | + /**
|
|
| 366 | + * Request a copy of the default/fallback bridge settings.
|
|
| 367 | + *
|
|
| 368 | + * @param {string[]} transports - A list of transports we support.
|
|
| 369 | + * @returns {MoatBridges[]} - The list of bridges found.
|
|
| 370 | + */
|
|
| 360 | 371 | async circumvention_defaults(transports) {
|
| 361 | 372 | const args = {
|
| 362 | 373 | transports: transports ? transports : [],
|
| ... | ... | @@ -367,7 +378,7 @@ export class MoatRPC { |
| 367 | 378 | const detail = response.errors[0].detail;
|
| 368 | 379 | throw new Error(`MoatRPC: ${detail} (${code})`);
|
| 369 | 380 | } else if ("settings" in response) {
|
| 370 | - return this.#fixupSettingsList(response.settings);
|
|
| 381 | + return this.#extractBridgesList(response.settings);
|
|
| 371 | 382 | }
|
| 372 | 383 | return [];
|
| 373 | 384 | }
|
| ... | ... | @@ -103,11 +103,12 @@ export const TorConnectTopics = Object.freeze({ |
| 103 | 103 | * failing bootstrap.
|
| 104 | 104 | * @property {integer} [options.simulateDelay] - The delay in microseconds to
|
| 105 | 105 | * apply to simulated bootstraps.
|
| 106 | - * @property {object} [options.simulateMoatResponse] - Simulate a Moat response
|
|
| 107 | - * for circumvention settings. Should include a "settings" property, and
|
|
| 108 | - * optionally a "country" property. You may add a "simulateCensorship"
|
|
| 109 | - * property to some of the settings to make only their bootstrap attempts
|
|
| 110 | - * fail.
|
|
| 106 | + * @property {MoatSettings} [options.simulateMoatResponse] - Simulate a Moat
|
|
| 107 | + * response for circumvention settings. Should include a "bridgesList"
|
|
| 108 | + * property, and optionally a "country" property. The "bridgesList" property
|
|
| 109 | + * should be an Array of MoatBridges objects that match the bridge settings
|
|
| 110 | + * accepted by TorSettings.bridges, plus you may add a "simulateCensorship"
|
|
| 111 | + * property to make only their bootstrap attempts fail.
|
|
| 111 | 112 | * @property {boolean} [options.testInternet] - Whether to also test the
|
| 112 | 113 | * internet connection.
|
| 113 | 114 | * @property {boolean} [options.simulateOffline] - Whether to simulate an
|
| ... | ... | @@ -214,6 +215,14 @@ class BootstrapAttempt { |
| 214 | 215 | return promise;
|
| 215 | 216 | }
|
| 216 | 217 | |
| 218 | + /**
|
|
| 219 | + * Method to call just after the Bootstrapped stage is set in response to this
|
|
| 220 | + * bootstrap attempt.
|
|
| 221 | + */
|
|
| 222 | + postBootstrapped() {
|
|
| 223 | + // Nothing to do.
|
|
| 224 | + }
|
|
| 225 | + |
|
| 217 | 226 | /**
|
| 218 | 227 | * Run the attempt.
|
| 219 | 228 | *
|
| ... | ... | @@ -395,17 +404,11 @@ class AutoBootstrapAttempt { |
| 395 | 404 | */
|
| 396 | 405 | #cancelledPromise = null;
|
| 397 | 406 | /**
|
| 398 | - * The found settings from Moat.
|
|
| 399 | - *
|
|
| 400 | - * @type {?object[]}
|
|
| 401 | - */
|
|
| 402 | - #settings = null;
|
|
| 403 | - /**
|
|
| 404 | - * The last settings that have been applied to the TorProvider, if any.
|
|
| 407 | + * The list of bridge configurations from Moat.
|
|
| 405 | 408 | *
|
| 406 | - * @type {?object}
|
|
| 409 | + * @type {?MoatBridges[]}
|
|
| 407 | 410 | */
|
| 408 | - #changedSetting = null;
|
|
| 411 | + #bridgesList = null;
|
|
| 409 | 412 | /**
|
| 410 | 413 | * The detected region code returned by Moat, if any.
|
| 411 | 414 | *
|
| ... | ... | @@ -438,13 +441,8 @@ class AutoBootstrapAttempt { |
| 438 | 441 | // Run cleanup before we resolve the promise to ensure two instances
|
| 439 | 442 | // of AutoBootstrapAttempt are not trying to change the settings at
|
| 440 | 443 | // the same time.
|
| 441 | - if (this.#changedSetting) {
|
|
| 442 | - if (arg.result === "complete") {
|
|
| 443 | - // Persist the current settings to preferences.
|
|
| 444 | - lazy.TorSettings.setSettings(this.#changedSetting);
|
|
| 445 | - lazy.TorSettings.saveToPrefs();
|
|
| 446 | - } // else, applySettings will restore the current settings.
|
|
| 447 | - await lazy.TorSettings.applySettings();
|
|
| 444 | + if (arg.result !== "complete") {
|
|
| 445 | + await lazy.TorSettings.clearTemporaryBridges();
|
|
| 448 | 446 | }
|
| 449 | 447 | } catch (error) {
|
| 450 | 448 | lazy.logger.error("Unexpected error in auto-bootstrap cleanup", error);
|
| ... | ... | @@ -466,6 +464,15 @@ class AutoBootstrapAttempt { |
| 466 | 464 | return promise;
|
| 467 | 465 | }
|
| 468 | 466 | |
| 467 | + /**
|
|
| 468 | + * Method to call just after the Bootstrapped stage is set in response to this
|
|
| 469 | + * bootstrap attempt.
|
|
| 470 | + */
|
|
| 471 | + postBootstrapped() {
|
|
| 472 | + // Persist the current settings to preferences.
|
|
| 473 | + lazy.TorSettings.saveTemporaryBridges();
|
|
| 474 | + }
|
|
| 475 | + |
|
| 469 | 476 | /**
|
| 470 | 477 | * Run the attempt.
|
| 471 | 478 | *
|
| ... | ... | @@ -477,12 +484,12 @@ class AutoBootstrapAttempt { |
| 477 | 484 | * @param {BootstrapOptions} options - Options to apply to the bootstrap.
|
| 478 | 485 | */
|
| 479 | 486 | async #runInternal(progressCallback, options) {
|
| 480 | - await this.#fetchSettings(options);
|
|
| 487 | + await this.#fetchBridges(options);
|
|
| 481 | 488 | if (this.#cancelled || this.#resolved) {
|
| 482 | 489 | return;
|
| 483 | 490 | }
|
| 484 | 491 | |
| 485 | - if (!this.#settings?.length) {
|
|
| 492 | + if (!this.#bridgesList?.length) {
|
|
| 486 | 493 | this.#resolveRun({
|
| 487 | 494 | error: new TorConnectError(
|
| 488 | 495 | options.regionCode === "automatic" && !this.detectedRegion
|
| ... | ... | @@ -493,14 +500,14 @@ class AutoBootstrapAttempt { |
| 493 | 500 | }
|
| 494 | 501 | |
| 495 | 502 | // Apply each of our settings and try to bootstrap with each.
|
| 496 | - for (const [index, currentSetting] of this.#settings.entries()) {
|
|
| 503 | + for (const [index, bridges] of this.#bridgesList.entries()) {
|
|
| 497 | 504 | lazy.logger.info(
|
| 498 | 505 | `Attempting Bootstrap with configuration ${index + 1}/${
|
| 499 | - this.#settings.length
|
|
| 506 | + this.#bridgesList.length
|
|
| 500 | 507 | }`
|
| 501 | 508 | );
|
| 502 | 509 | |
| 503 | - await this.#trySetting(currentSetting, progressCallback, options);
|
|
| 510 | + await this.#tryBridges(bridges, progressCallback, options);
|
|
| 504 | 511 | |
| 505 | 512 | if (this.#cancelled || this.#resolved) {
|
| 506 | 513 | return;
|
| ... | ... | @@ -518,7 +525,7 @@ class AutoBootstrapAttempt { |
| 518 | 525 | *
|
| 519 | 526 | * @param {BootstrapOptions} options - Options to apply to the bootstrap.
|
| 520 | 527 | */
|
| 521 | - async #fetchSettings(options) {
|
|
| 528 | + async #fetchBridges(options) {
|
|
| 522 | 529 | if (options.simulateMoatResponse) {
|
| 523 | 530 | await Promise.race([
|
| 524 | 531 | new Promise(res => setTimeout(res, options.simulateDelay || 0)),
|
| ... | ... | @@ -530,7 +537,7 @@ class AutoBootstrapAttempt { |
| 530 | 537 | }
|
| 531 | 538 | |
| 532 | 539 | this.detectedRegion = options.simulateMoatResponse.country || null;
|
| 533 | - this.#settings = options.simulateMoatResponse.settings ?? null;
|
|
| 540 | + this.#bridgesList = options.simulateMoatResponse.bridgesList ?? null;
|
|
| 534 | 541 | |
| 535 | 542 | return;
|
| 536 | 543 | }
|
| ... | ... | @@ -564,16 +571,16 @@ class AutoBootstrapAttempt { |
| 564 | 571 | |
| 565 | 572 | this.detectedRegion = maybeSettings?.country || null;
|
| 566 | 573 | |
| 567 | - if (maybeSettings?.settings?.length) {
|
|
| 568 | - this.#settings = maybeSettings.settings;
|
|
| 574 | + if (maybeSettings?.bridgesList?.length) {
|
|
| 575 | + this.#bridgesList = maybeSettings.bridgesList;
|
|
| 569 | 576 | } else {
|
| 570 | 577 | // Keep consistency with the other call.
|
| 571 | - this.#settings = await Promise.race([
|
|
| 578 | + this.#bridgesList = await Promise.race([
|
|
| 572 | 579 | moat.circumvention_defaults([
|
| 573 | 580 | ...lazy.TorSettings.builtinBridgeTypes,
|
| 574 | 581 | "vanilla",
|
| 575 | 582 | ]),
|
| 576 | - // This might set this.#settings to undefined.
|
|
| 583 | + // This might set this.#bridgesList to undefined.
|
|
| 577 | 584 | this.#cancelledPromise,
|
| 578 | 585 | ]);
|
| 579 | 586 | }
|
| ... | ... | @@ -586,21 +593,21 @@ class AutoBootstrapAttempt { |
| 586 | 593 | /**
|
| 587 | 594 | * Try to apply the settings we fetched.
|
| 588 | 595 | *
|
| 589 | - * @param {object} setting - The setting to try.
|
|
| 596 | + * @param {MoatBridges} bridges - The bridges to try.
|
|
| 590 | 597 | * @param {ProgressCallback} progressCallback - The callback to invoke with
|
| 591 | 598 | * the bootstrap progress.
|
| 592 | 599 | * @param {BootstrapOptions} options - Options to apply to the bootstrap.
|
| 593 | 600 | */
|
| 594 | - async #trySetting(setting, progressCallback, options) {
|
|
| 601 | + async #tryBridges(bridges, progressCallback, options) {
|
|
| 595 | 602 | if (this.#cancelled || this.#resolved) {
|
| 596 | 603 | return;
|
| 597 | 604 | }
|
| 598 | 605 | |
| 599 | - if (options.simulateMoatResponse && setting.simulateCensorship) {
|
|
| 606 | + if (options.simulateMoatResponse && bridges.simulateCensorship) {
|
|
| 600 | 607 | // Move the simulateCensorship option to the options for the next
|
| 601 | 608 | // BootstrapAttempt.
|
| 602 | - setting = structuredClone(setting);
|
|
| 603 | - delete setting.simulateCensorship;
|
|
| 609 | + bridges = structuredClone(bridges);
|
|
| 610 | + delete bridges.simulateCensorship;
|
|
| 604 | 611 | options = { ...options, simulateCensorship: true };
|
| 605 | 612 | }
|
| 606 | 613 | |
| ... | ... | @@ -616,14 +623,7 @@ class AutoBootstrapAttempt { |
| 616 | 623 | // Another idea (maybe easier to implement) is to disable the settings
|
| 617 | 624 | // UI while *any* bootstrap is going on.
|
| 618 | 625 | // This is also documented in tor-browser#41921.
|
| 619 | - const provider = await lazy.TorProviderBuilder.build();
|
|
| 620 | - this.#changedSetting = setting;
|
|
| 621 | - // We need to merge with old settings, in case the user is using a proxy
|
|
| 622 | - // or is behind a firewall.
|
|
| 623 | - await provider.writeSettings({
|
|
| 624 | - ...lazy.TorSettings.getSettings(),
|
|
| 625 | - ...setting,
|
|
| 626 | - });
|
|
| 626 | + await lazy.TorSettings.applyTemporaryBridges(bridges);
|
|
| 627 | 627 | |
| 628 | 628 | if (this.#cancelled || this.#resolved) {
|
| 629 | 629 | return;
|
| ... | ... | @@ -642,7 +642,7 @@ class AutoBootstrapAttempt { |
| 642 | 642 | error instanceof TorConnectError &&
|
| 643 | 643 | error.code === TorConnectError.BootstrapError
|
| 644 | 644 | ) {
|
| 645 | - lazy.logger.info("TorConnect setting failed", setting, error);
|
|
| 645 | + lazy.logger.info("TorConnect setting failed", bridges, error);
|
|
| 646 | 646 | // Try with the next settings.
|
| 647 | 647 | // NOTE: We do not restore the user settings in between these runs.
|
| 648 | 648 | // Instead we wait for #resolveRun callback to do so.
|
| ... | ... | @@ -1459,6 +1459,12 @@ export const TorConnect = { |
| 1459 | 1459 | }
|
| 1460 | 1460 | this._setStage(TorConnectStage.Bootstrapped);
|
| 1461 | 1461 | Services.obs.notifyObservers(null, TorConnectTopics.BootstrapComplete);
|
| 1462 | + |
|
| 1463 | + // Now call the postBootstrapped method. We do this after changing the
|
|
| 1464 | + // stage to ensure that AutoBootstrapAttempt.postBootstrapped call to
|
|
| 1465 | + // saveTemporaryBridges does not trigger SettingsChanged too early and
|
|
| 1466 | + // cancel itself.
|
|
| 1467 | + bootstrapAttempt.postBootstrapped();
|
|
| 1462 | 1468 | return;
|
| 1463 | 1469 | }
|
| 1464 | 1470 |
| ... | ... | @@ -198,6 +198,14 @@ class TorSettingsImpl { |
| 198 | 198 | allowed_ports: [],
|
| 199 | 199 | },
|
| 200 | 200 | };
|
| 201 | + |
|
| 202 | + /**
|
|
| 203 | + * Temporary bridge settings to apply instead of #settings.bridges.
|
|
| 204 | + *
|
|
| 205 | + * @type {?Object}
|
|
| 206 | + */
|
|
| 207 | + #temporaryBridgeSettings = null;
|
|
| 208 | + |
|
| 201 | 209 | /**
|
| 202 | 210 | * Accumulated errors from trying to set settings.
|
| 203 | 211 | *
|
| ... | ... | @@ -713,6 +721,8 @@ class TorSettingsImpl { |
| 713 | 721 | try {
|
| 714 | 722 | this.#allowUninitialized = true;
|
| 715 | 723 | this.#loadFromPrefs();
|
| 724 | + // We do not pass on the loaded settings to the TorProvider yet. Instead
|
|
| 725 | + // TorProvider will ask for these once it has initialised.
|
|
| 716 | 726 | } finally {
|
| 717 | 727 | this.#allowUninitialized = false;
|
| 718 | 728 | this.#notificationQueue.clear();
|
| ... | ... | @@ -971,7 +981,7 @@ class TorSettingsImpl { |
| 971 | 981 | async applySettings() {
|
| 972 | 982 | this.#checkIfInitialized();
|
| 973 | 983 | const provider = await lazy.TorProviderBuilder.build();
|
| 974 | - await provider.writeSettings(this.getSettings());
|
|
| 984 | + await provider.writeSettings();
|
|
| 975 | 985 | }
|
| 976 | 986 | |
| 977 | 987 | /**
|
| ... | ... | @@ -1073,12 +1083,20 @@ class TorSettingsImpl { |
| 1073 | 1083 | /**
|
| 1074 | 1084 | * Get a copy of all our settings.
|
| 1075 | 1085 | *
|
| 1086 | + * @param {boolean} [useTemporary=false] - Whether the returned settings
|
|
| 1087 | + * should use the temporary bridge settings, if any, instead.
|
|
| 1088 | + *
|
|
| 1076 | 1089 | * @returns {object} A copy of the settings object
|
| 1077 | 1090 | */
|
| 1078 | - getSettings() {
|
|
| 1091 | + getSettings(useTemporary = false) {
|
|
| 1079 | 1092 | lazy.logger.debug("getSettings()");
|
| 1080 | 1093 | this.#checkIfInitialized();
|
| 1081 | - return structuredClone(this.#settings);
|
|
| 1094 | + const settings = structuredClone(this.#settings);
|
|
| 1095 | + if (useTemporary && this.#temporaryBridgeSettings) {
|
|
| 1096 | + // Override the bridge settings with our temporary ones.
|
|
| 1097 | + settings.bridges = structuredClone(this.#temporaryBridgeSettings);
|
|
| 1098 | + }
|
|
| 1099 | + return settings;
|
|
| 1082 | 1100 | }
|
| 1083 | 1101 | |
| 1084 | 1102 | /**
|
| ... | ... | @@ -1097,6 +1115,83 @@ class TorSettingsImpl { |
| 1097 | 1115 | }
|
| 1098 | 1116 | return types;
|
| 1099 | 1117 | }
|
| 1118 | + |
|
| 1119 | + /**
|
|
| 1120 | + * Apply some Moat bridges temporarily.
|
|
| 1121 | + *
|
|
| 1122 | + * These bridges will not yet be saved to settings.
|
|
| 1123 | + *
|
|
| 1124 | + * @param {MoatBridges} bridges - The bridges to apply.
|
|
| 1125 | + */
|
|
| 1126 | + async applyTemporaryBridges(bridges) {
|
|
| 1127 | + this.#checkIfInitialized();
|
|
| 1128 | + |
|
| 1129 | + if (
|
|
| 1130 | + bridges.source !== TorBridgeSource.BuiltIn &&
|
|
| 1131 | + bridges.source !== TorBridgeSource.BridgeDB
|
|
| 1132 | + ) {
|
|
| 1133 | + throw new Error(`Invalid bridge source ${bridges.source}`);
|
|
| 1134 | + }
|
|
| 1135 | + |
|
| 1136 | + const bridgeSettings = {
|
|
| 1137 | + enabled: true,
|
|
| 1138 | + source: bridges.source,
|
|
| 1139 | + };
|
|
| 1140 | + |
|
| 1141 | + if (bridges.source === TorBridgeSource.BuiltIn) {
|
|
| 1142 | + if (!bridges.builtin_type) {
|
|
| 1143 | + throw Error("Missing a built-in type");
|
|
| 1144 | + }
|
|
| 1145 | + bridgeSettings.builtin_type = String(bridges.builtin_type);
|
|
| 1146 | + const bridgeStrings = this.getBuiltinBridges(bridgeSettings.builtin_type);
|
|
| 1147 | + if (!bridgeStrings.length) {
|
|
| 1148 | + throw new Error(`No builtin bridges for type ${bridges.builtin_type}`);
|
|
| 1149 | + }
|
|
| 1150 | + bridgeSettings.bridge_strings = bridgeStrings;
|
|
| 1151 | + } else {
|
|
| 1152 | + // BridgeDB.
|
|
| 1153 | + if (!bridges.bridge_strings?.length) {
|
|
| 1154 | + throw new Error("Missing bridges strings");
|
|
| 1155 | + }
|
|
| 1156 | + // TODO: Can we safely verify the format of the bridge addresses sent from
|
|
| 1157 | + // Moat?
|
|
| 1158 | + bridgeSettings.bridge_strings = Array.from(bridges.bridge_strings, item =>
|
|
| 1159 | + String(item)
|
|
| 1160 | + );
|
|
| 1161 | + }
|
|
| 1162 | + |
|
| 1163 | + // After checks are complete, we commit them.
|
|
| 1164 | + this.#temporaryBridgeSettings = bridgeSettings;
|
|
| 1165 | + await this.applySettings();
|
|
| 1166 | + }
|
|
| 1167 | + |
|
| 1168 | + /**
|
|
| 1169 | + * Save to current temporary bridges to be permanent instead.
|
|
| 1170 | + */
|
|
| 1171 | + async saveTemporaryBridges() {
|
|
| 1172 | + this.#checkIfInitialized();
|
|
| 1173 | + if (!this.#temporaryBridgeSettings) {
|
|
| 1174 | + lazy.logger.warn("No temporary bridges to save");
|
|
| 1175 | + return;
|
|
| 1176 | + }
|
|
| 1177 | + this.setSettings({ bridges: this.#temporaryBridgeSettings });
|
|
| 1178 | + this.#temporaryBridgeSettings = null;
|
|
| 1179 | + this.saveToPrefs();
|
|
| 1180 | + await this.applySettings();
|
|
| 1181 | + }
|
|
| 1182 | + |
|
| 1183 | + /**
|
|
| 1184 | + * Clear the current temporary bridges.
|
|
| 1185 | + */
|
|
| 1186 | + async clearTemporaryBridges() {
|
|
| 1187 | + this.#checkIfInitialized();
|
|
| 1188 | + if (!this.#temporaryBridgeSettings) {
|
|
| 1189 | + lazy.logger.debug("No temporary bridges to clear");
|
|
| 1190 | + return;
|
|
| 1191 | + }
|
|
| 1192 | + this.#temporaryBridgeSettings = null;
|
|
| 1193 | + await this.applySettings();
|
|
| 1194 | + }
|
|
| 1100 | 1195 | }
|
| 1101 | 1196 | |
| 1102 | 1197 | export const TorSettings = new TorSettingsImpl(); |