... |
... |
@@ -795,7 +795,64 @@ export class TorController { |
795
|
795
|
* @returns {Bridge[]} The configured bridges
|
796
|
796
|
*/
|
797
|
797
|
async getBridges() {
|
798
|
|
- return (await this.#getConf("BRIDGE")).map(TorParsers.parseBridgeLine);
|
|
798
|
+ let missingId = false;
|
|
799
|
+ const bridges = (await this.#getConf("BRIDGE")).map(line => {
|
|
800
|
+ const info = TorParsers.parseBridgeLine(line);
|
|
801
|
+ if (!info.id) {
|
|
802
|
+ missingId = true;
|
|
803
|
+ }
|
|
804
|
+ return info;
|
|
805
|
+ });
|
|
806
|
+
|
|
807
|
+ // tor-browser#42541: bridge lines are allowed not to have a fingerprint.
|
|
808
|
+ // If such a bridge is in use, we will fail to associate it to the circuits,
|
|
809
|
+ // and the circuit display will not be shown.
|
|
810
|
+ // Tor provides a couple of GETINFO commands we can try to use to get more
|
|
811
|
+ // data about bridges, in particular GETINFO ns/purpose/bridge.
|
|
812
|
+ // While it tells us the bridge's IP (as configured by the user, which might
|
|
813
|
+ // be different from the real one with some PTs such as Snowflake), it does
|
|
814
|
+ // not tell the pluggable transport.
|
|
815
|
+ // Therefore, we need to start from the configured bridge lines, and if we
|
|
816
|
+ // detect that a bridge does not have a fingerprint, we try to associate one
|
|
817
|
+ // through its IP address and port.
|
|
818
|
+ // However, users can set them directly, therefore we might end up setting
|
|
819
|
+ // a fingerprint to the wrong line (e.g., if the IP address is reused).
|
|
820
|
+ // Also, we are not sure about when the data of ns/purpose/bridge is
|
|
821
|
+ // populated.
|
|
822
|
+ // Usually, we are interested only in the data of currently active bridges
|
|
823
|
+ // for the circuit display. So, as a matter of fact, we expect to have
|
|
824
|
+ // entries and to expose only the correct and working data in the frontend.
|
|
825
|
+ if (missingId) {
|
|
826
|
+ // See https://spec.torproject.org/dir-spec/consensus-formats.html.
|
|
827
|
+ // r <name> <identity> <digest> <date> <time> <address> <orport> <dirport>
|
|
828
|
+ const info = (await this.#getInfo("ns/purpose/bridge")).matchAll(
|
|
829
|
+ /^r\s+\S+\s+(?<identity>\S+)\s+\S+\s+\S+\s+\S+\s+(?<address>\S+)\s+(?<orport>\d+)/gm
|
|
830
|
+ );
|
|
831
|
+ const b64ToHex = b64 => {
|
|
832
|
+ let hex = "";
|
|
833
|
+ const raw = atob(b64);
|
|
834
|
+ for (let i = 0; i < raw.length; i++) {
|
|
835
|
+ hex += raw.charCodeAt(i).toString(16).toUpperCase().padStart(2, "0");
|
|
836
|
+ }
|
|
837
|
+ return hex;
|
|
838
|
+ };
|
|
839
|
+ const knownBridges = new Map(
|
|
840
|
+ Array.from(info, m => [
|
|
841
|
+ `${m.groups.address}:${m.groups.orport}`,
|
|
842
|
+ b64ToHex(m.groups.identity),
|
|
843
|
+ ])
|
|
844
|
+ );
|
|
845
|
+ for (const b of bridges) {
|
|
846
|
+ if (!b.id) {
|
|
847
|
+ // We expect the addresses of these lines to be only IPv4, therefore
|
|
848
|
+ // we do not check for brackets, even though they might be matched by
|
|
849
|
+ // our regex.
|
|
850
|
+ b.id = knownBridges.get(b.addr) ?? "";
|
|
851
|
+ }
|
|
852
|
+ }
|
|
853
|
+ }
|
|
854
|
+
|
|
855
|
+ return bridges;
|
799
|
856
|
}
|
800
|
857
|
|
801
|
858
|
/**
|