Skip to content
Snippets Groups Projects
Verified Commit 08bc6b26 authored by Pier Angelo Vendrame's avatar Pier Angelo Vendrame :jack_o_lantern:
Browse files

fixup! Bug 40597: Implement TorSettings module

Bug 42343: Read built-in bridges from pt_config.json instead of
preferences.
parent 440a0aae
Branches
Tags
2 merge requests!898Bug 42252: Add further support for TorController in firefox-android,!877Bug 42343: Read built-in bridges from pt_config.json instead of preferences.
......@@ -137,3 +137,5 @@ toolkit.jar:
# Third party files
content/global/third_party/d3/d3.js (/third_party/js/d3/d3.js)
content/global/third_party/cfworker/json-schema.js (/third_party/js/cfworker/json-schema.js)
content/global/pt_config.json (pt_config.json)
{
"recommendedDefault" : "obfs4",
"pluggableTransports" : {
"lyrebird" : "ClientTransportPlugin meek_lite,obfs2,obfs3,obfs4,scramblesuit exec ${pt_path}lyrebird${pt_extension}",
"snowflake" : "ClientTransportPlugin snowflake exec ${pt_path}snowflake-client${pt_extension}",
"webtunnel" : "ClientTransportPlugin webtunnel exec ${pt_path}webtunnel-client${pt_extension}",
"conjure" : "ClientTransportPlugin conjure exec ${pt_path}conjure-client${pt_extension} -registerURL https://registration.refraction.network/api"
},
"bridges" : {
"meek-azure" : [
"meek_lite 192.0.2.18:80 BE776A53492E1E044A26F17306E1BC46A55A1625 url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com"
],
"obfs4" : [
"obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1",
"obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0",
"obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0",
"obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0",
"obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0",
"obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0",
"obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0",
"obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0",
"obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0",
"obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0",
"obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0"
],
"snowflake" : [
"snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=foursquare.com ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn",
"snowflake 192.0.2.4:80 8838024498816A039FCBBAB14E6F40A0843051FA fingerprint=8838024498816A039FCBBAB14E6F40A0843051FA url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=foursquare.com ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.net:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn"
]
}
}
......@@ -23,7 +23,6 @@ import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs"
import {
TorSettings,
TorSettingsTopics,
TorBuiltinBridgeTypes,
} from "resource://gre/modules/TorSettings.sys.mjs";
import { TorStrings } from "resource://gre/modules/TorStrings.sys.mjs";
......@@ -609,7 +608,7 @@ export const TorConnect = (() => {
}
const settings = await this.mrpc.circumvention_settings(
[...TorBuiltinBridgeTypes, "vanilla"],
[...TorSettings.builtinBridgeTypes, "vanilla"],
countryCode
);
......@@ -625,7 +624,7 @@ export const TorConnect = (() => {
} else {
try {
this.settings = await this.mrpc.circumvention_defaults([
...TorBuiltinBridgeTypes,
...TorSettings.builtinBridgeTypes,
"vanilla",
]);
} catch (err) {
......
......@@ -67,16 +67,6 @@ const TorSettingsPrefs = Object.freeze({
},
});
/* Legacy tor-launcher prefs and pref branches*/
const TorLauncherPrefs = Object.freeze({
quickstart: "extensions.torlauncher.quickstart",
default_bridge_type: "extensions.torlauncher.default_bridge_type",
default_bridge: "extensions.torlauncher.default_bridge.",
default_bridge_recommended_type:
"extensions.torlauncher.default_bridge_recommended_type",
bridgedb_bridge: "extensions.torlauncher.bridgedb_bridge.",
});
/* Config Keys used to configure tor daemon */
const TorConfigKeys = Object.freeze({
useBridges: "UseBridges",
......@@ -105,101 +95,41 @@ export const TorProxyType = Object.freeze({
HTTPS: 2,
});
export const TorBuiltinBridgeTypes = Object.freeze(
(() => {
const bridgeListBranch = Services.prefs.getBranch(
TorLauncherPrefs.default_bridge
);
const bridgePrefs = bridgeListBranch.getChildList("");
// an unordered set for shoving bridge types into
const bridgeTypes = new Set();
// look for keys ending in ".N" and treat string before that as the bridge type
const pattern = /\.[0-9]+$/;
for (const key of bridgePrefs) {
const offset = key.search(pattern);
if (offset != -1) {
const bt = key.substring(0, offset);
bridgeTypes.add(bt);
}
}
// recommended bridge type goes first in the list
const recommendedBridgeType = Services.prefs.getCharPref(
TorLauncherPrefs.default_bridge_recommended_type,
null
);
const retval = [];
if (recommendedBridgeType && bridgeTypes.has(recommendedBridgeType)) {
retval.push(recommendedBridgeType);
}
for (const bridgeType of bridgeTypes.values()) {
if (bridgeType != recommendedBridgeType) {
retval.push(bridgeType);
}
}
return retval;
})()
);
/* Parsing Methods */
// expects a '\n' or '\r\n' delimited bridge string, which we split and trim
// each bridge string can also optionally have 'bridge' at the beginning ie:
// bridge $(type) $(address):$(port) $(certificate)
// we strip out the 'bridge' prefix here
const parseBridgeStrings = function (aBridgeStrings) {
/**
* Split a blob of bridge lines into an array with single lines.
* Lines are delimited by \r\n or \n and each bridge string can also optionally
* have 'bridge' at the beginning.
* We split the text by \r\n, we trim the lines, remove the bridge prefix and
* filter out any remaiing empty item.
*
* @param {string} aBridgeStrings The text with the lines
* @returns {string[]} An array where each bridge line is an item
*/
function parseBridgeStrings(aBridgeStrings) {
// replace carriage returns ('\r') with new lines ('\n')
aBridgeStrings = aBridgeStrings.replace(/\r/g, "\n");
// then replace contiguous new lines ('\n') with a single one
aBridgeStrings = aBridgeStrings.replace(/[\n]+/g, "\n");
// split on the newline and for each bridge string: trim, remove starting 'bridge' string
// finally discard entries that are empty strings; empty strings could occur if we receive
// a new line containing only whitespace
// Split on the newline and for each bridge string: trim, remove starting
// 'bridge' string.
// Finally, discard entries that are empty strings; empty strings could occur
// if we receive a new line containing only whitespace.
const splitStrings = aBridgeStrings.split("\n");
return splitStrings
.map(val => val.trim().replace(/^bridge\s+/i, ""))
.filter(bridgeString => bridgeString != "");
};
const getBuiltinBridgeStrings = function (builtinType) {
if (!builtinType) {
return [];
.filter(bridgeString => bridgeString !== "");
}
const bridgeBranch = Services.prefs.getBranch(
TorLauncherPrefs.default_bridge
);
const bridgeBranchPrefs = bridgeBranch.getChildList("");
const retval = [];
// regex matches against strings ending in ".N" where N is a positive integer
const pattern = /\.[0-9]+$/;
for (const key of bridgeBranchPrefs) {
// verify the location of the match is the correct offset required for aBridgeType
// to fit, and that the string begins with aBridgeType
if (
key.search(pattern) == builtinType.length &&
key.startsWith(builtinType)
) {
const bridgeStr = bridgeBranch.getCharPref(key);
retval.push(bridgeStr);
}
}
// shuffle so that Tor Browser users don't all try the built-in bridges in the same order
arrayShuffle(retval);
return retval;
};
/* Helper methods */
const arrayShuffle = function (array) {
// fisher-yates shuffle
/**
* Return a shuffled (Fisher-Yates) copy of an array.
*
* @template T
* @param {T[]} array
* @returns {T[]}
*/
function arrayShuffle(array) {
array = [...array];
for (let i = array.length - 1; i > 0; --i) {
// number n such that 0.0 <= n < 1.0
const n = Math.random();
......@@ -211,7 +141,8 @@ const arrayShuffle = function (array) {
array[i] = array[j];
array[j] = tmp;
}
};
return array;
}
/* TorSettings module */
......@@ -245,6 +176,32 @@ class TorSettingsImpl {
},
};
/**
* The recommended pluggable transport.
*
* @type {string}
*/
#recommendedPT = "";
/**
* The bridge lines for built-in bridges.
* Keys are pluggable transports, and values are arrays of bridge lines.
*
* @type {Object.<string, string[]>}
*/
#builtinBridges = {};
#initComplete;
#initFailed;
#initialized = false;
constructor() {
this.initializedPromise = new Promise((resolve, reject) => {
this.#initComplete = resolve;
this.#initFailed = reject;
})
}
/**
* The current number of freezes applied to the notifications.
*
......@@ -419,8 +376,40 @@ class TorSettingsImpl {
return val1.every((v, i) => v === val2[i]);
}
/* load or init our settings, and register observers */
/**
* Return the bridge lines associated to a certain pluggable transport.
*
* @param {string} pt The pluggable transport to return the lines for
* @returns {string[]} The bridge lines in random order
*/
#getBuiltinBridges(pt) {
// Shuffle so that Tor Browser users do not all try the built-in bridges in
// the same order.
return arrayShuffle(this.#builtinBridges[pt] ?? []);
}
async init() {
if (this.#initialized) {
return;
}
try {
await this.#initInternal();
this.#initialized = true;
this.#initComplete();
} catch (e) {
this.#initFailed(e);
throw e;
}
}
get initialized() {
return this.#initialized;
}
/**
* Load or init our settings, and register observers.
*/
async #initInternal() {
this.#addProperties("quickstart", {
enabled: {},
});
......@@ -454,7 +443,7 @@ class TorSettingsImpl {
}
return;
}
const bridgeStrings = getBuiltinBridgeStrings(val);
const bridgeStrings = this.#getBuiltinBridges(val);
if (bridgeStrings.length) {
this.bridges.bridge_strings = bridgeStrings;
return;
......@@ -549,6 +538,15 @@ class TorSettingsImpl {
},
});
try {
const req = await fetch("chrome://global/content/pt_config.json");
const config = await req.json();
this.#recommendedPT = config.recommendedDefault;
this.#builtinBridges = config.bridges;
} catch (e) {
lazy.logger.error("Could not load the built-in PT config.", e);
}
// TODO: We could use a shared promise, and wait for it to be fullfilled
// instead of Service.obs.
if (lazy.TorLauncherUtil.shouldStartAndOwnTor) {
......@@ -883,11 +881,31 @@ class TorSettingsImpl {
lazy.logger.debug("setSettings result", this.#settings);
}
// get a copy of all our settings
/**
* Get a copy of all our settings
*
* @returns {object} A copy of the settings object
*/
getSettings() {
lazy.logger.debug("getSettings()");
return structuredClone(this.#settings);
}
/**
* Return an array with the pluggable transports for which we have at least a
* built-in bridge line.
*
* @returns {string[]} An array with PT identifiers
*/
get builtinBridgeTypes() {
const types = Object.keys(this.#builtinBridges);
const recommendedIndex = types.indexOf(this.#recommendedPT);
if (recommendedIndex > 0) {
types.splice(recommendedIndex, 1);
types.unshift(this.#recommendedPT);
}
return types;
}
}
export const TorSettings = new TorSettingsImpl();
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment