Pier Angelo Vendrame pushed to branch tor-browser-128.4.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
-
82705e37
by Pier Angelo Vendrame at 2024-11-12T17:32:04+01:00
-
163c91b6
by Pier Angelo Vendrame at 2024-11-12T17:32:05+01:00
-
f7e4e221
by Pier Angelo Vendrame at 2024-11-12T17:32:06+01:00
-
bb3cd92e
by Pier Angelo Vendrame at 2024-11-12T17:32:06+01:00
6 changed files:
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java
- toolkit/components/tor-launcher/TorControlPort.sys.mjs
- toolkit/components/tor-launcher/TorLauncherUtil.sys.mjs
- toolkit/components/tor-launcher/TorProcess.sys.mjs
- toolkit/components/tor-launcher/TorProcessAndroid.sys.mjs
- toolkit/components/tor-launcher/TorProvider.sys.mjs
Changes:
... | ... | @@ -220,12 +220,18 @@ public class TorIntegrationAndroid implements BundleEventListener { |
220 | 220 | if (previousProcess != null) {
|
221 | 221 | Log.w(TAG, "We still have a running process: " + previousProcess.getHandle());
|
222 | 222 | }
|
223 | - mTorProcess = new TorProcess(handle);
|
|
223 | + |
|
224 | + boolean tcpSocks = message.getBoolean("tcpSocks", false);
|
|
225 | + mTorProcess = new TorProcess(handle, tcpSocks);
|
|
224 | 226 | |
225 | 227 | GeckoBundle bundle = new GeckoBundle(3);
|
226 | 228 | bundle.putString("controlPortPath", mIpcDirectory + CONTROL_PORT_FILE);
|
227 | - bundle.putString("socksPath", mIpcDirectory + SOCKS_FILE);
|
|
228 | 229 | bundle.putString("cookieFilePath", mIpcDirectory + COOKIE_AUTH_FILE);
|
230 | + if (tcpSocks) {
|
|
231 | + bundle.putInt("socksPort", 0);
|
|
232 | + } else {
|
|
233 | + bundle.putString("socksPath", mIpcDirectory + SOCKS_FILE);
|
|
234 | + }
|
|
229 | 235 | callback.sendSuccess(bundle);
|
230 | 236 | }
|
231 | 237 | |
... | ... | @@ -254,10 +260,12 @@ public class TorIntegrationAndroid implements BundleEventListener { |
254 | 260 | private static final String EVENT_TOR_START_FAILED = "GeckoView:Tor:TorStartFailed";
|
255 | 261 | private static final String EVENT_TOR_EXITED = "GeckoView:Tor:TorExited";
|
256 | 262 | private final String mHandle;
|
263 | + private final boolean mTcpSocks;
|
|
257 | 264 | private Process mProcess = null;
|
258 | 265 | |
259 | - TorProcess(String handle) {
|
|
266 | + TorProcess(String handle, boolean tcpSocks) {
|
|
260 | 267 | mHandle = handle;
|
268 | + mTcpSocks = tcpSocks;
|
|
261 | 269 | setName("tor-process-" + handle);
|
262 | 270 | start();
|
263 | 271 | }
|
... | ... | @@ -273,8 +281,13 @@ public class TorIntegrationAndroid implements BundleEventListener { |
273 | 281 | args.add("1");
|
274 | 282 | args.add("+__ControlPort");
|
275 | 283 | args.add("unix:" + ipcDir + CONTROL_PORT_FILE);
|
284 | + final String socksFlags = " IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth";
|
|
276 | 285 | args.add("+__SocksPort");
|
277 | - args.add("unix:" + ipcDir + SOCKS_FILE + " IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth");
|
|
286 | + args.add("unix:" + ipcDir + SOCKS_FILE + socksFlags);
|
|
287 | + if (mTcpSocks) {
|
|
288 | + args.add("+__SocksPort");
|
|
289 | + args.add("auto " + socksFlags);
|
|
290 | + }
|
|
278 | 291 | args.add("CookieAuthentication");
|
279 | 292 | args.add("1");
|
280 | 293 | args.add("CookieAuthFile");
|
... | ... | @@ -298,6 +298,12 @@ class AsyncSocket { |
298 | 298 | * @property {string} [options] Optional options passed to the binary (only for
|
299 | 299 | * exec)
|
300 | 300 | */
|
301 | +/**
|
|
302 | + * @typedef {object} SocksListener
|
|
303 | + * @property {string} [ipcPath] path to a Unix socket to use for an IPC proxy
|
|
304 | + * @property {string} [host] The host to connect for a TCP proxy
|
|
305 | + * @property {number} [port] The port number to use for a TCP proxy
|
|
306 | + */
|
|
301 | 307 | /**
|
302 | 308 | * @typedef {object} OnionAuthKeyInfo
|
303 | 309 | * @property {string} address The address of the onion service
|
... | ... | @@ -746,6 +752,32 @@ export class TorController { |
746 | 752 | return this.#getInfo(`ip-to-country/${ip}`);
|
747 | 753 | }
|
748 | 754 | |
755 | + /**
|
|
756 | + * Ask tor which ports it is listening to for SOCKS connections.
|
|
757 | + *
|
|
758 | + * @returns {Promise<SocksListener[]>} An array of addresses. It might be
|
|
759 | + * empty (e.g., when DisableNetwork is set)
|
|
760 | + */
|
|
761 | + async getSocksListeners() {
|
|
762 | + const listeners = await this.#getInfo("net/listeners/socks");
|
|
763 | + return Array.from(
|
|
764 | + listeners.matchAll(/\s*("(?:[^"\\]|\\.)*"|\S+)\s*/g),
|
|
765 | + m => {
|
|
766 | + const listener = TorParsers.unescapeString(m[1]);
|
|
767 | + if (listener.startsWith("unix:/")) {
|
|
768 | + return { ipcPath: listener.substring(5) };
|
|
769 | + }
|
|
770 | + const idx = listener.lastIndexOf(":");
|
|
771 | + const host = listener.substring(0, idx);
|
|
772 | + const port = parseInt(listener.substring(idx + 1));
|
|
773 | + if (isNaN(port) || port <= 0 || port > 65535 || !host || !port) {
|
|
774 | + throw new Error(`Could not parse the SOCKS listener ${listener}.`);
|
|
775 | + }
|
|
776 | + return { host, port };
|
|
777 | + }
|
|
778 | + );
|
|
779 | + }
|
|
780 | + |
|
749 | 781 | /**
|
750 | 782 | * Ask Tor a list of circuits.
|
751 | 783 | *
|
... | ... | @@ -449,7 +449,7 @@ export const TorLauncherUtil = Object.freeze({ |
449 | 449 | * If network.proxy.socks contains a file: URL, a default value of
|
450 | 450 | * "127.0.0.1" is used instead.
|
451 | 451 | * If the network.proxy.socks_port value is not valid (outside the
|
452 | - * (0; 65535] range), a default value of 9150 is used instead.
|
|
452 | + * (0; 65535] range), we will let the tor daemon choose a port.
|
|
453 | 453 | *
|
454 | 454 | * The SOCKS configuration will not influence the launch of a tor daemon and
|
455 | 455 | * the configuration of the control port in any way.
|
... | ... | @@ -458,13 +458,6 @@ export const TorLauncherUtil = Object.freeze({ |
458 | 458 | * This also applies to TOR_TRANSPROXY (at least for now): tor will be
|
459 | 459 | * launched with its defaults.
|
460 | 460 | *
|
461 | - * TODO: add a preference to ignore the current configuration, and let tor
|
|
462 | - * listen on any free port. Then, the browser will prompt the daemon the port
|
|
463 | - * to use through the control port (even though this is quite dangerous at the
|
|
464 | - * moment, because with network disabled tor will disable also the SOCKS
|
|
465 | - * listeners, so it means that we will have to check it every time we change
|
|
466 | - * the network status).
|
|
467 | - *
|
|
468 | 461 | * @returns {SocksSettings}
|
469 | 462 | */
|
470 | 463 | getPreferredSocksConfiguration() {
|
... | ... | @@ -491,7 +484,7 @@ export const TorLauncherUtil = Object.freeze({ |
491 | 484 | }
|
492 | 485 | if (Services.env.exists("TOR_SOCKS_PORT")) {
|
493 | 486 | const port = parseInt(Services.env.get("TOR_SOCKS_PORT"), 10);
|
494 | - if (Number.isInteger(port) && port > 0 && port <= 65535) {
|
|
487 | + if (Number.isInteger(port) && port >= 0 && port <= 65535) {
|
|
495 | 488 | socksPortInfo.port = port;
|
496 | 489 | useIPC = false;
|
497 | 490 | }
|
... | ... | @@ -522,20 +515,32 @@ export const TorLauncherUtil = Object.freeze({ |
522 | 515 | socksPortInfo.host = socksAddrHasHost ? socksAddr : "127.0.0.1";
|
523 | 516 | }
|
524 | 517 | |
525 | - if (!socksPortInfo.port) {
|
|
518 | + if (socksPortInfo.port === undefined) {
|
|
526 | 519 | let socksPort = Services.prefs.getIntPref(
|
527 | 520 | "network.proxy.socks_port",
|
528 | - 0
|
|
521 | + 9150
|
|
529 | 522 | );
|
530 | - // This pref is set as 0 by default in Firefox, use 9150 if we get 0.
|
|
531 | - socksPortInfo.port =
|
|
532 | - socksPort > 0 && socksPort <= 65535 ? socksPort : 9150;
|
|
523 | + if (socksPort > 0 && socksPort <= 65535) {
|
|
524 | + socksPortInfo.port = socksPort;
|
|
525 | + } else {
|
|
526 | + // Automatic port number, we have to query tor over the control port
|
|
527 | + // every time we change DisableNetwork.
|
|
528 | + socksPortInfo.port = 0;
|
|
529 | + }
|
|
533 | 530 | }
|
534 | 531 | }
|
535 | 532 | |
536 | 533 | return socksPortInfo;
|
537 | 534 | },
|
538 | 535 | |
536 | + /**
|
|
537 | + * Apply our proxy configuration to the browser.
|
|
538 | + *
|
|
539 | + * Currently, we try to configure the Tor daemon to match the browser's
|
|
540 | + * configuration, but this might change in the future (tor-browser#42062).
|
|
541 | + *
|
|
542 | + * @param {SocksSettings} socksPortInfo The configuration to apply
|
|
543 | + */
|
|
539 | 544 | setProxyConfiguration(socksPortInfo) {
|
540 | 545 | if (socksPortInfo.transproxy) {
|
541 | 546 | Services.prefs.setBoolPref("network.proxy.socks_remote_dns", false);
|
... | ... | @@ -556,7 +561,7 @@ export const TorLauncherUtil = Object.freeze({ |
556 | 561 | if (socksPortInfo.host) {
|
557 | 562 | Services.prefs.setCharPref("network.proxy.socks", socksPortInfo.host);
|
558 | 563 | }
|
559 | - if (socksPortInfo.port) {
|
|
564 | + if (socksPortInfo.port > 0 && socksPortInfo.port <= 65535) {
|
|
560 | 565 | Services.prefs.setIntPref(
|
561 | 566 | "network.proxy.socks_port",
|
562 | 567 | socksPortInfo.port
|
... | ... | @@ -53,13 +53,16 @@ export class TorProcess { |
53 | 53 | throw new Error("Unauthenticated control port is not supported");
|
54 | 54 | }
|
55 | 55 | |
56 | - const checkPort = port =>
|
|
56 | + const checkPort = (port, allowZero) =>
|
|
57 | 57 | port === undefined ||
|
58 | - (Number.isInteger(port) && port > 0 && port < 65535);
|
|
59 | - if (!checkPort(controlSettings?.port)) {
|
|
58 | + (Number.isInteger(port) &&
|
|
59 | + port < 65535 &&
|
|
60 | + (port > 0 || (allowZero && port === 0)));
|
|
61 | + if (!checkPort(controlSettings?.port, false)) {
|
|
60 | 62 | throw new Error("Invalid control port");
|
61 | 63 | }
|
62 | - if (!checkPort(socksSettings.port)) {
|
|
64 | + // Port 0 for SOCKS means automatic port.
|
|
65 | + if (!checkPort(socksSettings.port, true)) {
|
|
63 | 66 | throw new Error("Invalid port specified for the SOCKS port");
|
64 | 67 | }
|
65 | 68 | |
... | ... | @@ -296,10 +299,12 @@ export class TorProcess { |
296 | 299 | let socksPortArg;
|
297 | 300 | if (this.#socksSettings.ipcFile) {
|
298 | 301 | socksPortArg = this.#socksSettings.ipcFile;
|
299 | - } else if (this.#socksSettings.port != 0) {
|
|
302 | + } else if (this.#socksSettings.port > 0) {
|
|
300 | 303 | socksPortArg = this.#socksSettings.host
|
301 | 304 | ? `${this.#socksSettings.host}:${this.#socksSettings.port}`
|
302 | 305 | : this.#socksSettings.port.toString();
|
306 | + } else {
|
|
307 | + socksPortArg = "auto";
|
|
303 | 308 | }
|
304 | 309 | if (socksPortArg) {
|
305 | 310 | const socksPortFlags = Services.prefs.getCharPref(
|
... | ... | @@ -77,6 +77,10 @@ export class TorProcessAndroid { |
77 | 77 | config = await lazy.EventDispatcher.instance.sendRequestForResult({
|
78 | 78 | type: TorOutgoingEvents.start,
|
79 | 79 | handle: this.#processHandle,
|
80 | + tcpSocks: Services.prefs.getBoolPref(
|
|
81 | + "extensions.torlauncher.socks_port_use_tcp",
|
|
82 | + false
|
|
83 | + ),
|
|
80 | 84 | });
|
81 | 85 | logger.debug("Sent the start event.");
|
82 | 86 | } catch (e) {
|
... | ... | @@ -27,23 +27,23 @@ const logger = console.createInstance({ |
27 | 27 | * @typedef {object} ControlPortSettings An object with the settings to use for
|
28 | 28 | * the control port. All the entries are optional, but an authentication
|
29 | 29 | * mechanism and a communication method must be specified.
|
30 | - * @property {Uint8Array=} password The clear text password as an array of
|
|
30 | + * @property {Uint8Array} [password] The clear text password as an array of
|
|
31 | 31 | * bytes. It must always be defined, unless cookieFilePath is
|
32 | - * @property {string=} cookieFilePath The path to the cookie file to use for
|
|
32 | + * @property {string} [cookieFilePath] The path to the cookie file to use for
|
|
33 | 33 | * authentication
|
34 | - * @property {nsIFile=} ipcFile The nsIFile object with the path to a Unix
|
|
34 | + * @property {nsIFile} [ipcFile] The nsIFile object with the path to a Unix
|
|
35 | 35 | * socket to use for control socket
|
36 | - * @property {string=} host The host to connect for a TCP control port
|
|
37 | - * @property {number=} port The port number to use for a TCP control port
|
|
36 | + * @property {string} [host] The host to connect for a TCP control port
|
|
37 | + * @property {number} [port] The port number to use for a TCP control port
|
|
38 | 38 | */
|
39 | 39 | /**
|
40 | 40 | * @typedef {object} SocksSettings An object that includes the proxy settings to
|
41 | 41 | * be configured in the browser.
|
42 | - * @property {boolean=} transproxy If true, no proxy is configured
|
|
43 | - * @property {nsIFile=} ipcFile The nsIFile object with the path to a Unix
|
|
42 | + * @property {boolean} [transproxy] If true, no proxy is configured
|
|
43 | + * @property {nsIFile} [ipcFile] The nsIFile object with the path to a Unix
|
|
44 | 44 | * socket to use for an IPC proxy
|
45 | - * @property {string=} host The host to connect for a TCP proxy
|
|
46 | - * @property {number=} port The port number to use for a TCP proxy
|
|
45 | + * @property {string} [host] The host to connect for a TCP proxy
|
|
46 | + * @property {number} [port] The port number to use for a TCP proxy
|
|
47 | 47 | */
|
48 | 48 | /**
|
49 | 49 | * @typedef {object} LogEntry An object with a log message
|
... | ... | @@ -345,6 +345,25 @@ export class TorProvider { |
345 | 345 | */
|
346 | 346 | async connect() {
|
347 | 347 | await this.#controller.setNetworkEnabled(true);
|
348 | + if (this.#socksSettings.port === 0) {
|
|
349 | + // Enablign/disabling network resets also the SOCKS listener.
|
|
350 | + // So, every time we do it, we need to update the browser's configuration
|
|
351 | + // to use the updated port.
|
|
352 | + const settings = structuredClone(this.#socksSettings);
|
|
353 | + for (const listener of await this.#controller.getSocksListeners()) {
|
|
354 | + // When set to automatic port, ignore any IPC listener, as the intention
|
|
355 | + // was to use TCP.
|
|
356 | + if (listener.ipcPath) {
|
|
357 | + continue;
|
|
358 | + }
|
|
359 | + // The tor daemon can have any number of SOCKS listeners (see SocksPort
|
|
360 | + // in man 1 tor). We take for granted that any TCP one will work for us.
|
|
361 | + settings.host = listener.host;
|
|
362 | + settings.port = listener.port;
|
|
363 | + break;
|
|
364 | + }
|
|
365 | + TorLauncherUtil.setProxyConfiguration(settings);
|
|
366 | + }
|
|
348 | 367 | this.#lastWarning = {};
|
349 | 368 | this.retrieveBootstrapStatus();
|
350 | 369 | }
|
... | ... | @@ -569,14 +588,24 @@ export class TorProvider { |
569 | 588 | logger.debug("Trying to start the tor process.");
|
570 | 589 | const res = await this.#torProcess.start();
|
571 | 590 | if (TorLauncherUtil.isAndroid) {
|
591 | + logger.debug("Configuration from TorProcessAndriod", res);
|
|
572 | 592 | this.#controlPortSettings = {
|
573 | 593 | ipcFile: new lazy.FileUtils.File(res.controlPortPath),
|
574 | 594 | cookieFilePath: res.cookieFilePath,
|
575 | 595 | };
|
576 | 596 | this.#socksSettings = {
|
577 | 597 | transproxy: false,
|
578 | - ipcFile: new lazy.FileUtils.File(res.socksPath),
|
|
579 | 598 | };
|
599 | + if (res.socksPath) {
|
|
600 | + this.#socksSettings.ipcFile = new lazy.FileUtils.File(res.socksPath);
|
|
601 | + } else if (res.socksPort !== undefined) {
|
|
602 | + this.#socksSettings.host = res.socksHost ?? "127.0.0.1";
|
|
603 | + this.#socksSettings.port = res.socksPort;
|
|
604 | + } else {
|
|
605 | + throw new Error(
|
|
606 | + "TorProcessAndroid did not return a valid SOCKS configuration."
|
|
607 | + );
|
|
608 | + }
|
|
580 | 609 | }
|
581 | 610 | logger.info("Started a tor process");
|
582 | 611 | }
|