[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-115.3.0esr-13.0-1] 2 commits: fixup! Bug 40933: Add tor-launcher functionality

richard (@richard) git at gitlab.torproject.org
Wed Sep 27 19:48:59 UTC 2023



richard pushed to branch tor-browser-115.3.0esr-13.0-1 at The Tor Project / Applications / Tor Browser


Commits:
14e1b34c by Pier Angelo Vendrame at 2023-09-27T19:39:39+00:00
fixup! Bug 40933: Add tor-launcher functionality

Bug 42131: Check for existing circuits during initialization.

- - - - -
4b7f4df4 by Pier Angelo Vendrame at 2023-09-27T19:39:39+00:00
fixup! Bug 40933: Add tor-launcher functionality

Bug 42132: Poll for circuit information when we did not collect its data
already.

- - - - -


2 changed files:

- toolkit/components/tor-launcher/TorControlPort.sys.mjs
- toolkit/components/tor-launcher/TorProvider.sys.mjs


Changes:

=====================================
toolkit/components/tor-launcher/TorControlPort.sys.mjs
=====================================
@@ -272,6 +272,11 @@ class AsyncSocket {
  *
  * @typedef {string} NodeFingerprint
  */
+/**
+ * @typedef {object} CircuitInfo
+ * @property {CircuitID} id
+ * @property {NodeFingerprint[]} nodes
+ */
 /**
  * @typedef {object} Bridge
  * @property {string} transport The transport of the bridge, or vanilla if not
@@ -729,12 +734,14 @@ export class TorController {
   /**
    * Ask Tor a list of circuits.
    *
-   * @returns {string[]} An array with a string for each line
+   * @returns {CircuitInfo[]} An array with a string for each line
    */
   async getCircuits() {
     const circuits = await this.#getInfo("circuit-status");
-    // TODO: Do more parsing once we move the event parsing to this class!
-    return circuits.split(/\r?\n/);
+    return circuits
+      .split(/\r?\n/)
+      .map(this.#parseCircBuilt.bind(this))
+      .filter(circ => circ);
   }
 
   // Configuration
@@ -1022,25 +1029,15 @@ export class TorController {
         this.#eventHandler.onBootstrapStatus(status);
         break;
       case "CIRC":
-        const builtEvent =
-          /^(?<ID>[a-zA-Z0-9]{1,16})\sBUILT\s(?<Path>(,?\$([0-9a-fA-F]{40})(?:~[a-zA-Z0-9]{1,19})?)+)/.exec(
-            data.groups.data
-          );
+        const maybeCircuit = this.#parseCircBuilt(data.groups.data);
         const closedEvent = /^(?<ID>[a-zA-Z0-9]{1,16})\sCLOSED/.exec(
           data.groups.data
         );
-        if (builtEvent) {
-          const fp = /\$([0-9a-fA-F]{40})/g;
-          const nodes = Array.from(builtEvent.groups.Path.matchAll(fp), g =>
-            g[1].toUpperCase()
+        if (maybeCircuit) {
+          this.#eventHandler.onCircuitBuilt(
+            maybeCircuit.id,
+            maybeCircuit.nodes
           );
-          // In some cases, we might already receive SOCKS credentials in the
-          // line. However, this might be a problem with onion services: we get
-          // also a 4-hop circuit that we likely do not want to show to the
-          // user, especially because it is used only temporarily, and it would
-          // need a technical explaination.
-          // const credentials = this.#parseCredentials(data.groups.data);
-          this.#eventHandler.onCircuitBuilt(builtEvent.groups.ID, nodes);
         } else if (closedEvent) {
           this.#eventHandler.onCircuitClosed(closedEvent.groups.ID);
         }
@@ -1068,7 +1065,7 @@ export class TorController {
     }
   }
 
-  // Other helpers
+  // Parsers
 
   /**
    * Parse a bootstrap status line.
@@ -1099,15 +1096,32 @@ export class TorController {
   }
 
   /**
-   * Throw an exception when value is not a string.
+   * Parse a CIRC BUILT event or a GETINFO circuit-status.
    *
-   * @param {any} value The value to check
-   * @param {string} name The name of the `value` argument
+   * @param {string} line The line to parse
+   * @returns {CircuitInfo?} The ID and nodes of the circuit, or null if the
+   * parsing failed.
    */
-  #expectString(value, name) {
-    if (typeof value !== "string" && !(value instanceof String)) {
-      throw new Error(`The ${name} argument is expected to be a string.`);
+  #parseCircBuilt(line) {
+    const builtEvent =
+      /^(?<ID>[a-zA-Z0-9]{1,16})\sBUILT\s(?<Path>(,?\$([0-9a-fA-F]{40})(?:~[a-zA-Z0-9]{1,19})?)+)/.exec(
+        line
+      );
+    if (!builtEvent) {
+      return null;
     }
+    const fp = /\$([0-9a-fA-F]{40})/g;
+    const nodes = Array.from(builtEvent.groups.Path.matchAll(fp), g =>
+      g[1].toUpperCase()
+    );
+    // In some cases, we might already receive SOCKS credentials in the
+    // line. However, this might be a problem with Onion services: we get
+    // also a 4-hop circuit that we likely do not want to show to the
+    // user, especially because it is used only temporarily, and it would
+    // need a technical explaination.
+    // So we do not try to extract them for now. Otherwise, we could do
+    // const credentials = this.#parseCredentials(line);
+    return { id: builtEvent.groups.ID, nodes };
   }
 
   /**
@@ -1146,6 +1160,20 @@ export class TorController {
       )
     );
   }
+
+  // Other helpers
+
+  /**
+   * Throw an exception when value is not a string.
+   *
+   * @param {any} value The value to check
+   * @param {string} name The name of the `value` argument
+   */
+  #expectString(value, name) {
+    if (typeof value !== "string" && !(value instanceof String)) {
+      throw new Error(`The ${name} argument is expected to be a string.`);
+    }
+  }
 }
 
 /**


=====================================
toolkit/components/tor-launcher/TorProvider.sys.mjs
=====================================
@@ -137,7 +137,7 @@ export class TorProvider {
    * built before the new identity but not yet used. If we cleaned the map, we
    * risked of not having the data about it.
    *
-   * @type {Map<CircuitID, NodeFingerprint[]>}
+   * @type {Map<CircuitID, Promise<NodeFingerprint[]>>}
    */
   #circuits = new Map();
   /**
@@ -204,6 +204,11 @@ export class TorProvider {
 
     logger.debug(`Notifying ${TorProviderTopics.ProcessIsReady}`);
     Services.obs.notifyObservers(null, TorProviderTopics.ProcessIsReady);
+
+    // If we are using an external Tor daemon, we might need to fetch circuits
+    // already, in case streams use them. Do not await because we do not want to
+    // block the intialization on this (it should not fail anyway...).
+    this.#fetchCircuits();
   }
 
   /**
@@ -799,6 +804,16 @@ export class TorProvider {
     return crypto.getRandomValues(new Uint8Array(kPasswordLen));
   }
 
+  /**
+   * Ask Tor the circuits it already knows to populate our circuit map with the
+   * circuits that were already open before we started listening for events.
+   */
+  async #fetchCircuits() {
+    for (const { id, nodes } of await this.#controller.getCircuits()) {
+      this.onCircuitBuilt(id, nodes);
+    }
+  }
+
   // Notification handlers
 
   /**
@@ -983,18 +998,35 @@ export class TorProvider {
    * @param {CircuitID} circuitId The ID of the circuit used by the stream
    * @param {string} username The SOCKS username
    * @param {string} password The SOCKS password
-   * @returns
    */
-  onStreamSucceeded(streamId, circuitId, username, password) {
+  async onStreamSucceeded(streamId, circuitId, username, password) {
     if (!username || !password) {
       return;
     }
     logger.debug("Stream succeeded event", username, password, circuitId);
-    const circuit = this.#circuits.get(circuitId);
+    let circuit = this.#circuits.get(circuitId);
     if (!circuit) {
-      logger.error(
-        "Seen a STREAM SUCCEEDED with an unknown circuit. Not notifying observers."
-      );
+      circuit = new Promise((resolve, reject) => {
+        this.#controlConnection.getCircuits().then(circuits => {
+          for (const { id, nodes } of circuits) {
+            if (id === circuitId) {
+              resolve(nodes);
+              return;
+            }
+            // Opportunistically collect circuits, since we are iterating them.
+            this.#circuits.set(id, nodes);
+          }
+          logger.error(
+            `Seen a STREAM SUCCEEDED with circuit ${circuitId}, but Tor did not send information about it.`
+          );
+          reject();
+        });
+      });
+      this.#circuits.set(circuitId, circuit);
+    }
+    try {
+      circuit = await circuit;
+    } catch {
       return;
     }
     Services.obs.notifyObservers(



View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/c978614edd6577dd449e591aa05a41e850d8c356...4b7f4df4ca05e86ff078310dbf6509c2da02326f

-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/c978614edd6577dd449e591aa05a41e850d8c356...4b7f4df4ca05e86ff078310dbf6509c2da02326f
You're receiving this email because of your account on gitlab.torproject.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.torproject.org/pipermail/tor-commits/attachments/20230927/db544bb9/attachment-0001.htm>


More information about the tor-commits mailing list