tbb-commits
Threads by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 1 participants
- 20970 discussions
[Git][tpo/applications/tor-browser-build][main] Bug 40920: Non-deterministic generation of baseline.profm file in Android apks
by richard (@richard) 11 Aug '23
by richard (@richard) 11 Aug '23
11 Aug '23
richard pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
e49c2819 by Richard Pospesel at 2023-08-11T18:13:49+00:00
Bug 40920: Non-deterministic generation of baseline.profm file in Android apks
- - - - -
3 changed files:
- projects/browser/build.android
- projects/browser/config
- + projects/browser/sort-baseline.py
Changes:
=====================================
projects/browser/build.android
=====================================
@@ -9,6 +9,11 @@ ext_dir=$assets_dir/extensions
qa_apk=[% dest_dir %]/[% c('filename') %]/[% c("var/project-name") %]-[% c("version") %]-[% c("var/osname") %]-multi-qa.apk
apk=$rootdir/firefox-android/*-[% c("var/abi") %]-*.apk
+# tor-browser-build#40920
+sorted_baseline_apk=$(basename $apk .apk)_sorted_baseline.apk
+$rootdir/sort-baseline.py --apk $apk $sorted_baseline_apk
+mv $sorted_baseline_apk $apk
+
# Bundle our extensioni(s).
# NoScript will be copied over to the profile folder
# as a "regular" browser extension receiving regular AMO updates.
=====================================
projects/browser/config
=====================================
@@ -151,3 +151,6 @@ input_files:
- project: manual
name: manual
enable: '[% ! c("var/android") && c("var/tor-browser") %]'
+ # tor-browser-build#40920
+ - filename: sort-baseline.py
+ enable: '[% c("var/android") %]'
=====================================
projects/browser/sort-baseline.py
=====================================
@@ -0,0 +1,185 @@
+#!/usr/bin/python3
+# encoding: utf-8
+# SPDX-FileCopyrightText: 2023 FC Stegerman <flx(a)obfusk.net>
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+import struct
+import zipfile
+import zlib
+
+from typing import Any, Dict, Tuple
+
+# https://android.googlesource.com/platform/tools/base
+# profgen/profgen/src/main/kotlin/com/android/tools/profgen/ArtProfileSerializer.kt
+
+PROF_MAGIC = b"pro\x00"
+PROFM_MAGIC = b"prm\x00"
+
+PROF_001_N = b"001\x00"
+PROF_005_O = b"005\x00"
+PROF_009_O_MR1 = b"009\x00"
+PROF_010_P = b"010\x00"
+PROF_015_S = b"015\x00"
+
+PROFM_001_N = b"001\x00"
+PROFM_002 = b"002\x00"
+
+ASSET_PROF = "assets/dexopt/baseline.prof"
+ASSET_PROFM = "assets/dexopt/baseline.profm"
+
+ATTRS = ("compress_type", "create_system", "create_version", "date_time",
+ "external_attr", "extract_version", "flag_bits")
+LEVELS = (9, 6, 4, 1)
+
+
+class Error(RuntimeError):
+ pass
+
+
+# FIXME: is there a better alternative?
+class ReproducibleZipInfo(zipfile.ZipInfo):
+ """Reproducible ZipInfo hack."""
+
+ if "_compresslevel" not in zipfile.ZipInfo.__slots__: # type: ignore[attr-defined]
+ raise Error("zipfile.ZipInfo has no ._compresslevel")
+
+ _compresslevel: int
+ _override: Dict[str, Any] = {}
+
+ def __init__(self, zinfo: zipfile.ZipInfo, **override: Any) -> None:
+ # pylint: disable=W0231
+ if override:
+ self._override = {**self._override, **override}
+ for k in self.__slots__:
+ if hasattr(zinfo, k):
+ setattr(self, k, getattr(zinfo, k))
+
+ def __getattribute__(self, name: str) -> Any:
+ if name != "_override":
+ try:
+ return self._override[name]
+ except KeyError:
+ pass
+ return object.__getattribute__(self, name)
+
+
+def sort_baseline(input_file: str, output_file: str) -> None:
+ with open(input_file, "rb") as fhi:
+ data = _sort_baseline(fhi.read())
+ with open(output_file, "wb") as fho:
+ fho.write(data)
+
+
+def sort_baseline_apk(input_apk: str, output_apk: str) -> None:
+ with open(input_apk, "rb") as fh_raw:
+ with zipfile.ZipFile(input_apk) as zf_in:
+ with zipfile.ZipFile(output_apk, "w") as zf_out:
+ for info in zf_in.infolist():
+ attrs = {attr: getattr(info, attr) for attr in ATTRS}
+ zinfo = ReproducibleZipInfo(info, **attrs)
+ if info.compress_type == 8:
+ fh_raw.seek(info.header_offset)
+ n, m = struct.unpack("<HH", fh_raw.read(30)[26:30])
+ fh_raw.seek(info.header_offset + 30 + m + n)
+ ccrc = 0
+ size = info.compress_size
+ while size > 0:
+ ccrc = zlib.crc32(fh_raw.read(min(size, 4096)), ccrc)
+ size -= 4096
+ with zf_in.open(info) as fh_in:
+ comps = {lvl: zlib.compressobj(lvl, 8, -15) for lvl in LEVELS}
+ ccrcs = {lvl: 0 for lvl in LEVELS}
+ while True:
+ data = fh_in.read(4096)
+ if not data:
+ break
+ for lvl in LEVELS:
+ ccrcs[lvl] = zlib.crc32(comps[lvl].compress(data), ccrcs[lvl])
+ for lvl in LEVELS:
+ if ccrc == zlib.crc32(comps[lvl].flush(), ccrcs[lvl]):
+ zinfo._compresslevel = lvl
+ break
+ else:
+ raise Error(f"Unable to determine compresslevel for {info.filename!r}")
+ elif info.compress_type != 0:
+ raise Error(f"Unsupported compress_type {info.compress_type}")
+ if info.filename == ASSET_PROFM:
+ print(f"replacing {info.filename!r}...")
+ zf_out.writestr(zinfo, _sort_baseline(zf_in.read(info)))
+ else:
+ with zf_in.open(info) as fh_in:
+ with zf_out.open(zinfo, "w") as fh_out:
+ while True:
+ data = fh_in.read(4096)
+ if not data:
+ break
+ fh_out.write(data)
+
+
+# FIXME
+# Supported .prof: none
+# Supported .profm: 002
+# Unsupported .profm: 001 N
+def _sort_baseline(data: bytes) -> bytes:
+ magic, data = _split(data, 4)
+ version, data = _split(data, 4)
+ if magic == PROF_MAGIC:
+ raise Error(f"Unsupported prof version {version!r}")
+ elif magic == PROFM_MAGIC:
+ if version == PROFM_002:
+ return PROFM_MAGIC + PROFM_002 + sort_profm_002(data)
+ else:
+ raise Error(f"Unsupported profm version {version!r}")
+ else:
+ raise Error(f"Unsupported magic {magic!r}")
+
+
+def sort_profm_002(data: bytes) -> bytes:
+ num_dex_files, uncompressed_data_size, compressed_data_size, data = _unpack("<HII", data)
+ profiles = []
+ if len(data) != compressed_data_size:
+ raise Error("Compressed data size does not match")
+ data = zlib.decompress(data)
+ if len(data) != uncompressed_data_size:
+ raise Error("Uncompressed data size does not match")
+ for _ in range(num_dex_files):
+ profile = data[:4]
+ profile_idx, profile_key_size, data = _unpack("<HH", data)
+ profile_key, data = _split(data, profile_key_size)
+ profile += profile_key + data[:6]
+ num_type_ids, num_class_ids, data = _unpack("<IH", data)
+ class_ids, data = _split(data, num_class_ids * 2)
+ profile += class_ids
+ profiles.append((profile_key, profile))
+ if data:
+ raise Error("Expected end of data")
+ srtd = b"".join(int.to_bytes(i, 2, "little") + p[1][2:]
+ for i, p in enumerate(sorted(profiles)))
+ cdata = zlib.compress(srtd, 1)
+ hdr = struct.pack("<HII", num_dex_files, uncompressed_data_size, len(cdata))
+ return hdr + cdata
+
+
+def _unpack(fmt: str, data: bytes) -> Any:
+ assert all(c in "<BHI" for c in fmt)
+ size = fmt.count("B") + 2 * fmt.count("H") + 4 * fmt.count("I")
+ return struct.unpack(fmt, data[:size]) + (data[size:],)
+
+
+def _split(data: bytes, size: int) -> Tuple[bytes, bytes]:
+ return data[:size], data[size:]
+
+
+if __name__ == "__main__":
+ import argparse
+ parser = argparse.ArgumentParser(prog="sort-baseline.py")
+ parser.add_argument("--apk", action="store_true")
+ parser.add_argument("input_prof_or_apk", metavar="INPUT_PROF_OR_APK")
+ parser.add_argument("output_prof_or_apk", metavar="OUTPUT_PROF_OR_APK")
+ args = parser.parse_args()
+ if args.apk:
+ sort_baseline_apk(args.input_prof_or_apk, args.output_prof_or_apk)
+ else:
+ sort_baseline(args.input_prof_or_apk, args.output_prof_or_apk)
+
+# vim: set tw=80 sw=4 sts=4 et fdm=marker :
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/e…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/e…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-update-responses][main] Revert "alpha: new version, 13.0a2"
by richard (@richard) 11 Aug '23
by richard (@richard) 11 Aug '23
11 Aug '23
richard pushed to branch main at The Tor Project / Applications / Tor Browser update responses
Commits:
c58afcb4 by Richard Pospesel at 2023-08-11T01:33:55+00:00
Revert "alpha: new version, 13.0a2"
This reverts commit 65ae6dcf728213d058d7741a7c1397a25caaac08.
- - - - -
30 changed files:
- update_3/alpha/.htaccess
- + update_3/alpha/12.5a5-13.0a1-linux32-ALL.xml
- + update_3/alpha/12.5a5-13.0a1-linux64-ALL.xml
- + update_3/alpha/12.5a5-13.0a1-macos-ALL.xml
- + update_3/alpha/12.5a5-13.0a1-win32-ALL.xml
- + update_3/alpha/12.5a5-13.0a1-win64-ALL.xml
- + update_3/alpha/12.5a6-13.0a1-linux32-ALL.xml
- + update_3/alpha/12.5a6-13.0a1-linux64-ALL.xml
- + update_3/alpha/12.5a6-13.0a1-macos-ALL.xml
- + update_3/alpha/12.5a6-13.0a1-win32-ALL.xml
- + update_3/alpha/12.5a6-13.0a1-win64-ALL.xml
- + update_3/alpha/12.5a7-13.0a1-linux32-ALL.xml
- + update_3/alpha/12.5a7-13.0a1-linux64-ALL.xml
- + update_3/alpha/12.5a7-13.0a1-macos-ALL.xml
- + update_3/alpha/12.5a7-13.0a1-win32-ALL.xml
- + update_3/alpha/12.5a7-13.0a1-win64-ALL.xml
- − update_3/alpha/13.0a1-13.0a2-linux32-ALL.xml
- − update_3/alpha/13.0a1-13.0a2-linux64-ALL.xml
- − update_3/alpha/13.0a1-13.0a2-macos-ALL.xml
- − update_3/alpha/13.0a1-13.0a2-win32-ALL.xml
- − update_3/alpha/13.0a1-13.0a2-win64-ALL.xml
- + update_3/alpha/13.0a1-linux32-ALL.xml
- + update_3/alpha/13.0a1-linux64-ALL.xml
- + update_3/alpha/13.0a1-macos-ALL.xml
- + update_3/alpha/13.0a1-win32-ALL.xml
- + update_3/alpha/13.0a1-win64-ALL.xml
- − update_3/alpha/13.0a2-linux32-ALL.xml
- − update_3/alpha/13.0a2-linux64-ALL.xml
- − update_3/alpha/13.0a2-macos-ALL.xml
- − update_3/alpha/13.0a2-win32-ALL.xml
The diff was not included because it is too large.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-update-responses…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-update-responses…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build] Pushed new tag tbb-13.0a2-build3
by richard (@richard) 11 Aug '23
by richard (@richard) 11 Aug '23
11 Aug '23
richard pushed new tag tbb-13.0a2-build3 at The Tor Project / Applications / tor-browser-build
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/tbb…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 40901: Prepare Tor Browser 13.0a2-build3
by richard (@richard) 11 Aug '23
by richard (@richard) 11 Aug '23
11 Aug '23
richard pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
f724d7f9 by Richard Pospesel at 2023-08-10T23:09:50+00:00
Bug 40901: Prepare Tor Browser 13.0a2-build3
fixes tor-browser#41996: App includes com.google.android.gms.permission.AD_ID permission
- - - - -
2 changed files:
- projects/firefox-android/config
- rbm.conf
Changes:
=====================================
projects/firefox-android/config
=====================================
@@ -16,7 +16,7 @@ container:
var:
fenix_version: 115.2.0
browser_branch: 13.0-1
- browser_build: 3
+ browser_build: 4
platform_version: 115.0
variant: Beta
# This should be updated when the list of gradle dependencies is changed.
=====================================
rbm.conf
=====================================
@@ -82,7 +82,7 @@ buildconf:
var:
torbrowser_version: '13.0a2'
- torbrowser_build: 'build2'
+ torbrowser_build: 'build3'
torbrowser_incremental_from:
- '13.0a1'
updater_enabled: 1
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/f…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/f…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/firefox-android] Pushed new tag firefox-android-115.2.0-13.0-1-build4
by richard (@richard) 11 Aug '23
by richard (@richard) 11 Aug '23
11 Aug '23
richard pushed new tag firefox-android-115.2.0-13.0-1-build4 at The Tor Project / Applications / firefox-android
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/firefox-android/-/tree/firef…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/firefox-android][firefox-android-115.2.0-13.0-1] fixup! Disable features and functionality
by richard (@richard) 11 Aug '23
by richard (@richard) 11 Aug '23
11 Aug '23
richard pushed to branch firefox-android-115.2.0-13.0-1 at The Tor Project / Applications / firefox-android
Commits:
c0580e65 by Dan Ballard at 2023-08-10T16:28:57-07:00
fixup! Disable features and functionality
Bug 41996: Remove android manifest permissions for Google AD_ID
- - - - -
3 changed files:
- fenix/app/build.gradle
- fenix/app/src/main/AndroidManifest.xml
- fenix/buildSrc/src/main/java/FenixDependencies.kt
Changes:
=====================================
fenix/app/build.gradle
=====================================
@@ -636,8 +636,6 @@ dependencies {
implementation FenixDependencies.adjust
implementation FenixDependencies.installreferrer // Required by Adjust
- implementation FenixDependencies.google_ads_id // Required for the Google Advertising ID
-
// Required for in-app reviews
implementation FenixDependencies.google_play_review
implementation FenixDependencies.google_play_review_ktx
=====================================
fenix/app/src/main/AndroidManifest.xml
=====================================
@@ -19,9 +19,6 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
- <!-- Needed for Google Play policy https://support.google.com/googleplay/android-developer/answer/6048248 -->
- <uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
-
<!-- Needed to prompt the user to give permission to install a downloaded apk -->
<uses-permission-sdk-23 android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
=====================================
fenix/buildSrc/src/main/java/FenixDependencies.kt
=====================================
@@ -61,8 +61,6 @@ object FenixVersions {
const val uiautomator = "2.2.0"
const val robolectric = "4.10.1"
- const val google_ads_id_version = "16.0.0"
-
const val google_play_review_version = "2.0.0"
// keep in sync with the versions used in AS.
@@ -169,8 +167,6 @@ object FenixDependencies {
const val uiautomator = "androidx.test.uiautomator:uiautomator:${FenixVersions.uiautomator}"
const val robolectric = "org.robolectric:robolectric:${FenixVersions.robolectric}"
- const val google_ads_id = "com.google.android.gms:play-services-ads-identifier:${FenixVersions.google_ads_id_version}"
-
// Required for in-app reviews
const val google_play_review = "com.google.android.play:review:${FenixVersions.google_play_review_version}"
const val google_play_review_ktx = "com.google.android.play:review-ktx:${FenixVersions.google_play_review_version}"
View it on GitLab: https://gitlab.torproject.org/tpo/applications/firefox-android/-/commit/c05…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/firefox-android/-/commit/c05…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-update-responses][main] alpha: new version, 13.0a2
by richard (@richard) 10 Aug '23
by richard (@richard) 10 Aug '23
10 Aug '23
richard pushed to branch main at The Tor Project / Applications / Tor Browser update responses
Commits:
65ae6dcf by Richard Pospesel at 2023-08-10T21:01:45+00:00
alpha: new version, 13.0a2
- - - - -
30 changed files:
- update_3/alpha/.htaccess
- − update_3/alpha/12.5a5-13.0a1-linux32-ALL.xml
- − update_3/alpha/12.5a5-13.0a1-linux64-ALL.xml
- − update_3/alpha/12.5a5-13.0a1-macos-ALL.xml
- − update_3/alpha/12.5a5-13.0a1-win32-ALL.xml
- − update_3/alpha/12.5a5-13.0a1-win64-ALL.xml
- − update_3/alpha/12.5a6-13.0a1-linux32-ALL.xml
- − update_3/alpha/12.5a6-13.0a1-linux64-ALL.xml
- − update_3/alpha/12.5a6-13.0a1-macos-ALL.xml
- − update_3/alpha/12.5a6-13.0a1-win32-ALL.xml
- − update_3/alpha/12.5a6-13.0a1-win64-ALL.xml
- − update_3/alpha/12.5a7-13.0a1-linux32-ALL.xml
- − update_3/alpha/12.5a7-13.0a1-linux64-ALL.xml
- − update_3/alpha/12.5a7-13.0a1-macos-ALL.xml
- − update_3/alpha/12.5a7-13.0a1-win32-ALL.xml
- − update_3/alpha/12.5a7-13.0a1-win64-ALL.xml
- + update_3/alpha/13.0a1-13.0a2-linux32-ALL.xml
- + update_3/alpha/13.0a1-13.0a2-linux64-ALL.xml
- + update_3/alpha/13.0a1-13.0a2-macos-ALL.xml
- + update_3/alpha/13.0a1-13.0a2-win32-ALL.xml
- + update_3/alpha/13.0a1-13.0a2-win64-ALL.xml
- − update_3/alpha/13.0a1-linux32-ALL.xml
- − update_3/alpha/13.0a1-linux64-ALL.xml
- − update_3/alpha/13.0a1-macos-ALL.xml
- − update_3/alpha/13.0a1-win32-ALL.xml
- − update_3/alpha/13.0a1-win64-ALL.xml
- + update_3/alpha/13.0a2-linux32-ALL.xml
- + update_3/alpha/13.0a2-linux64-ALL.xml
- + update_3/alpha/13.0a2-macos-ALL.xml
- + update_3/alpha/13.0a2-win32-ALL.xml
The diff was not included because it is too large.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-update-responses…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-update-responses…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 40918: Add the commit hash also when merging AARs.
by richard (@richard) 10 Aug '23
by richard (@richard) 10 Aug '23
10 Aug '23
richard pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
a7137058 by Pier Angelo Vendrame at 2023-08-10T17:24:57+00:00
Bug 40918: Add the commit hash also when merging AARs.
We added the commit hashes to GeckoView. However, we might have tested
it only with the single-arch testbuilds, because it is not displayed in
regular builds.
Adding the information also to the mozconfig-android-all makes the
commit link available also on them.
- - - - -
2 changed files:
- projects/geckoview/build
- projects/geckoview/merge_aars
Changes:
=====================================
projects/geckoview/build
=====================================
@@ -80,6 +80,9 @@ mkdir "$HOME/.mozbuild"
cat >> mozconfig-android-all << 'MOZCONFIG_EOF'
ac_add_options --enable-update-channel=[% c("var/channel") %]
ac_add_options --with-base-browser-version=[% c("var/torbrowser_version") %]
+export MOZ_INCLUDE_SOURCE_INFO=1
+export MOZ_SOURCE_REPO="[% c('var/gitlab_project') %]"
+export MOZ_SOURCE_CHANGESET=[% c("var/git_commit") %]
MOZCONFIG_EOF
pushd tools/torbrowser
=====================================
projects/geckoview/merge_aars
=====================================
@@ -27,7 +27,12 @@ export MOZ_ANDROID_FAT_AAR_X86_64=$builddir/geckoview/*x86_64*.aar
cd $builddir/[% project %]-[% c("version") %]
ln -s mozconfig-android-all .mozconfig
-echo 'mk_add_options MOZ_PARALLEL_BUILD=[% c("num_procs") %]' >> .mozconfig
+cat >> mozconfig-android-all << 'MOZCONFIG_EOF'
+mk_add_options MOZ_PARALLEL_BUILD=[% c("num_procs") %]
+export MOZ_INCLUDE_SOURCE_INFO=1
+export MOZ_SOURCE_REPO="[% c('var/gitlab_project') %]"
+export MOZ_SOURCE_CHANGESET=[% c("var/git_commit") %]
+MOZCONFIG_EOF
[% c("var/set_MOZ_BUILD_DATE") %]
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/a…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/a…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build] Pushed new tag tbb-13.0a2-build2
by Pier Angelo Vendrame (@pierov) 10 Aug '23
by Pier Angelo Vendrame (@pierov) 10 Aug '23
10 Aug '23
Pier Angelo Vendrame pushed new tag tbb-13.0a2-build2 at The Tor Project / Applications / tor-browser-build
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/tbb…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 40901: Prepare Tor Browser 13.0a2-build2
by richard (@richard) 10 Aug '23
by richard (@richard) 10 Aug '23
10 Aug '23
richard pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
15afa277 by Pier Angelo Vendrame at 2023-08-10T19:19:11+02:00
Bug 40901: Prepare Tor Browser 13.0a2-build2
- - - - -
3 changed files:
- projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
- projects/firefox-android/config
- rbm.conf
Changes:
=====================================
projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
=====================================
@@ -21,7 +21,9 @@ Tor Browser 13.0a2 - August 08 2023
* Bug 41964: 'emojiAnnotations' not defined in time in connection preferences [tor-browser]
* Android
* Updated GeckoView to 115.1.0esr
+ * Bug 40919: Fix nimbus-fml reproducibility of 13.0a2-build1 [tor-browser-build]
* Bug 41928: Backport Android-specific security fixes from Firefox 116 to ESR 102.14 / 115.1 - based Tor Browser [tor-browser]
+ * Bug 41972: Disable Firefox onboarding in 13.0 [tor-browser]
* Build System
* All Platforms
* Updated Go to 1.20.7
@@ -39,6 +41,7 @@ Tor Browser 13.0a2 - August 08 2023
* Bug 31546: Create and expose PDB files for Tor Browser debugging on Windows [tor-browser-build]
* Android
* Bug 40867: Create a RBM project for the unified Android repository [tor-browser-build]
+ * Bug 40917: Remove the uniffi-rs project [tor-browser-build]
* Bug 41899: Use LLD for Android [tor-browser]
Tor Browser 12.5.2 - July 31 2023
=====================================
projects/firefox-android/config
=====================================
@@ -16,7 +16,7 @@ container:
var:
fenix_version: 115.2.0
browser_branch: 13.0-1
- browser_build: 2
+ browser_build: 3
platform_version: 115.0
variant: Beta
# This should be updated when the list of gradle dependencies is changed.
=====================================
rbm.conf
=====================================
@@ -82,7 +82,7 @@ buildconf:
var:
torbrowser_version: '13.0a2'
- torbrowser_build: 'build1'
+ torbrowser_build: 'build2'
torbrowser_incremental_from:
- '13.0a1'
updater_enabled: 1
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/1…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/1…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 40917: Remove the uniffi-rs project.
by richard (@richard) 10 Aug '23
by richard (@richard) 10 Aug '23
10 Aug '23
richard pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
64cb7b18 by Pier Angelo Vendrame at 2023-08-10T16:26:11+00:00
Bug 40917: Remove the uniffi-rs project.
We build an old version of uniffi-rs which is consumed only by
application-services.
However, AS includes a newer version in its Cargo.lock and it is
automatically added to the vendor archive, so it can build completely
fine also without this project.
So, we can remove it.
- - - - -
6 changed files:
- Makefile
- projects/application-services/build
- projects/application-services/config
- − projects/uniffi-rs/btreeset.patch
- − projects/uniffi-rs/build
- − projects/uniffi-rs/config
Changes:
=====================================
Makefile
=====================================
@@ -583,9 +583,6 @@ cargo_vendor-application-services: submodule-update
cargo_vendor-cbindgen: submodule-update
$(rbm) build cbindgen --step cargo_vendor --target alpha --target torbrowser-linux-x86_64
-cargo_vendor-uniffi-rs: submodule-update
- $(rbm) build uniffi-rs --step cargo_vendor --target nightly --target torbrowser-linux-x86_64
-
go_vendor-snowflake-alpha: submodule-update
$(rbm) build snowflake --step go_vendor --target alpha --target torbrowser-linux-x86_64
=====================================
projects/application-services/build
=====================================
@@ -21,8 +21,7 @@ mkdir /var/tmp/build
cd /var/tmp/dist
tar -xf $rootdir/[% c('input_files_by_name/rust') %]
tar -xf $rootdir/[% c('input_files_by_name/ninja') %]
-tar -xf $rootdir/[% c('input_files_by_name/uniffi-rs') %]
-export PATH=/var/tmp/dist/rust/bin:/var/tmp/dist/ninja:/var/tmp/dist/uniffi-rs:$PATH
+export PATH=/var/tmp/dist/rust/bin:/var/tmp/dist/ninja:$PATH
export RUST_ANDROID_GRADLE_PYTHON_COMMAND=python3
cd $rootdir
=====================================
projects/application-services/config
=====================================
@@ -46,9 +46,6 @@ steps:
- project: ninja
name: ninja
pkg_type: build
- - project: uniffi-rs
- name: uniffi-rs
- pkg_type: build
# See libs/build-all.sh to update these!
# Also, build them with application-services, since they need the NDK and
# we are using a different one from the other projects.
=====================================
projects/uniffi-rs/btreeset.patch deleted
=====================================
@@ -1,31 +0,0 @@
-diff --git a/uniffi_bindgen/src/interface/types.rs b/uniffi_bindgen/src/interface/types.rs
-index 6448d58..b7efd22 100644
---- a/uniffi_bindgen/src/interface/types.rs
-+++ b/uniffi_bindgen/src/interface/types.rs
-@@ -26,7 +26,7 @@
- use anyhow::bail;
- use anyhow::Result;
- use std::convert::TryFrom;
--use std::{collections::hash_map::Entry, collections::HashMap, collections::HashSet};
-+use std::{collections::hash_map::Entry, collections::HashMap, collections::BTreeSet};
-
- use super::Attributes;
-
-@@ -71,7 +71,7 @@ pub enum FFIType {
- /// Represents all the different high-level types that can be used in a component interface.
- /// At this level we identify user-defined types by name, without knowing any details
- /// of their internal structure apart from what type of thing they are (record, enum, etc).
--#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-+#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
- pub enum Type {
- // Primitive types.
- UInt8,
-@@ -195,7 +195,7 @@ pub(crate) struct TypeUniverse {
- // Named type definitions (including aliases).
- type_definitions: HashMap<String, Type>,
- // All the types in the universe, by canonical type name.
-- all_known_types: HashSet<Type>,
-+ all_known_types: BTreeSet<Type>,
- }
-
- impl TypeUniverse {
=====================================
projects/uniffi-rs/build deleted
=====================================
@@ -1,42 +0,0 @@
-#!/bin/bash
-[% c("var/set_default_env") -%]
-distdir=/var/tmp/dist
-builddir=/var/tmp/build/[% project %]
-mkdir -p $distdir/[% project %]
-tar -C $distdir -xf $rootdir/[% c('input_files_by_name/rust') %]
-export PATH="/var/tmp/dist/rust/bin:$PATH"
-mkdir -p /var/tmp/build
-tar -C /var/tmp/build -xf [% project %]-[% c('version') %].tar.[% c('compress_tar') %]
-
-# Now prepare the offline build
-# Move the directory for hardcoding the path in .cargo/config
-mv /var/tmp/build/[% project %]-[% c('version') %] $builddir
-tar -C $builddir -xjf uniffi-rs-vendor-[% c('version') %].tar.bz2
-cd $builddir
-mkdir .cargo
-cat > .cargo/config << 'EOF'
-[source.crates-io]
-replace-with = "vendored-sources"
-
-[source.vendored-sources]
-directory = "/var/tmp/build/uniffi-rs/vendor"
-EOF
-
-# We change the data type of the `all_known_types` Set from HashSet to BTreeSet.
-# Uniffi iterates over the elements of this set, and iteration over a HashSet occurs
-# in an arbitrary order, while iteration over a BTreeSet orders in a defined (and
-# deterministic) order. This patch solves a build reproducibility issue, see
-# tor-browser-build#40208.
-#
-# Upstream bug: https://github.com/mozilla/uniffi-rs/issues/374
-patch -p1 < $rootdir/btreeset.patch
-# We usually use --frozen but there is no Cargo.lock file available. Thus resort
-# to --offline.
-cargo build --release --offline --target x86_64-unknown-linux-gnu
-mv target/x86_64-unknown-linux-gnu/release/uniffi-bindgen $distdir/[% project %]
-
-cd $distdir
-[% c('tar', {
- tar_src => [ project ],
- tar_args => '-caf ' _ dest_dir _ '/' _ c('filename'),
- }) %]
=====================================
projects/uniffi-rs/config deleted
=====================================
@@ -1,27 +0,0 @@
-# vim: filetype=yaml sw=2
-version: 0.7.0
-git_url: https://github.com/mozilla/uniffi-rs
-git_hash: ea3ff0402438ef1ebceda4c5fbbbd2ed6a9be227
-filename: '[% project %]-[% c("version") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]'
-
-container:
- use_container: 1
-
-input_files:
- - project: container-image
- - name: rust
- project: rust
- # Use `make cargo_vendor-uniffi-rs` to re-generate the vendor tarball
- - URL: https://people.torproject.org/~boklm/mirrors/sources/uniffi-rs-vendor-[% c('version') %].tar.bz2
- sha256sum: 8f201df618b3e7bcaaf01a87e0d55171605b80abeb1b660fe4dd0b9bfc10a0d8
- - filename: btreeset.patch
-
-steps:
- cargo_vendor:
- filename: '[% project %]-vendor-[% c("version") %].tar.bz2'
- input_files:
- - project: container-image
- pkg_type: build
- - project: rust
- name: rust
- pkg_type: build
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/6…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/6…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 40919: Fix nimbus-fml reproducibility problems.
by richard (@richard) 10 Aug '23
by richard (@richard) 10 Aug '23
10 Aug '23
richard pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
8f52ed5f by Pier Angelo Vendrame at 2023-08-10T10:05:42+02:00
Bug 40919: Fix nimbus-fml reproducibility problems.
- - - - -
1 changed file:
- projects/application-services/bug40485.diff
Changes:
=====================================
projects/application-services/bug40485.diff
=====================================
@@ -1,5 +1,31 @@
+diff --git a/components/support/nimbus-fml/src/intermediate_representation.rs b/components/support/nimbus-fml/src/intermediate_representation.rs
+index e61f8a728..c1e396cab 100644
+--- a/components/support/nimbus-fml/src/intermediate_representation.rs
++++ b/components/support/nimbus-fml/src/intermediate_representation.rs
+@@ -8,7 +8,7 @@ use crate::util::loaders::FilePath;
+ use anyhow::{bail, Error, Result as AnyhowResult};
+ use serde::{Deserialize, Serialize};
+ use serde_json::{Map, Value};
+-use std::collections::{BTreeSet, HashMap, HashSet};
++use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+ use std::fmt::Display;
+ use std::slice::Iter;
+
+@@ -191,10 +191,10 @@ pub struct FeatureManifest {
+ pub(crate) about: AboutBlock,
+
+ #[serde(default)]
+- pub(crate) imported_features: HashMap<ModuleId, BTreeSet<String>>,
++ pub(crate) imported_features: BTreeMap<ModuleId, BTreeSet<String>>,
+
+ #[serde(default)]
+- pub(crate) all_imports: HashMap<ModuleId, FeatureManifest>,
++ pub(crate) all_imports: BTreeMap<ModuleId, FeatureManifest>,
+ }
+
+ impl TypeFinder for FeatureManifest {
diff --git a/components/support/nimbus-fml/src/parser.rs b/components/support/nimbus-fml/src/parser.rs
-index bb676f827..d00b1b6ef 100644
+index bb676f827..0d7e78583 100644
--- a/components/support/nimbus-fml/src/parser.rs
+++ b/components/support/nimbus-fml/src/parser.rs
@@ -26,7 +26,7 @@ pub(crate) struct EnumVariantBody {
@@ -41,6 +67,33 @@ index bb676f827..d00b1b6ef 100644
#[serde(default)]
#[serde(alias = "include")]
+@@ -785,7 +785,7 @@ impl Parser {
+ &self,
+ current: &FilePath,
+ channel: &str,
+- imports: &mut HashMap<ModuleId, FeatureManifest>,
++ imports: &mut BTreeMap<ModuleId, FeatureManifest>,
+ ) -> Result<ModuleId> {
+ let id = current.try_into()?;
+ if imports.contains_key(&id) {
+@@ -814,7 +814,7 @@ impl Parser {
+ // This loop does the work of merging the default blocks back into the imported manifests.
+ // We'll then attach all the manifests to the root (i.e. the one we're generating code for today), in `imports`.
+ // We associate only the feature ids with the manifest we're loading in this method.
+- let mut imported_feature_id_map = HashMap::new();
++ let mut imported_feature_id_map = BTreeMap::new();
+
+ for block in &frontend.imports {
+ // 1. Load the imported manifests in to the hash map.
+@@ -888,7 +888,7 @@ impl Parser {
+ &self,
+ channel: &str,
+ ) -> Result<FeatureManifest, FMLError> {
+- let mut manifests = HashMap::new();
++ let mut manifests = BTreeMap::new();
+ let id = self.load_imports(&self.source, channel, &mut manifests)?;
+ let mut fm = manifests
+ .remove(&id)
@@ -1009,12 +1009,12 @@ impl Parser {
}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/8…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/8…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build] Pushed new tag mb-13.0a2-build1
by richard (@richard) 09 Aug '23
by richard (@richard) 09 Aug '23
09 Aug '23
richard pushed new tag mb-13.0a2-build1 at The Tor Project / Applications / tor-browser-build
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/mb-…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build] Pushed new tag tbb-13.0a2-build1
by richard (@richard) 09 Aug '23
by richard (@richard) 09 Aug '23
09 Aug '23
richard pushed new tag tbb-13.0a2-build1 at The Tor Project / Applications / tor-browser-build
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/tbb…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] 4 commits: Bug 40908: Enable the --enable-gpl config flag in tor to bring in PoW functionality
by richard (@richard) 09 Aug '23
by richard (@richard) 09 Aug '23
09 Aug '23
richard pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
ec97c9d3 by Richard Pospesel at 2023-08-09T12:38:17+02:00
Bug 40908: Enable the --enable-gpl config flag in tor to bring in PoW functionality
- - - - -
9f038fb2 by Nicolas Vigier at 2023-08-09T12:38:19+02:00
Bug 40916: Pull Mullvad mar files from archive.torproject.org
In order to generate incrementals, get mar files from the previous
version from archive.tpo instead of the Mullvad mirrors, since that
release is not always there but should always be on archive.tpo.
- - - - -
da34986c by Pier Angelo Vendrame at 2023-08-09T17:14:21+02:00
MB 199 (fix): Use -L to copy Mullvad Browser's Fluent files.
We need to use the -L option when copying MB's Fluent files because on
macOS we symlink ja-JP-mac to ja.
- - - - -
c70c38d5 by Richard Pospesel at 2023-08-09T17:16:07+02:00
Bug 40901, 40902: Prepare Tor+Mullvad Browser Alpha 13.0a2
- - - - -
15 changed files:
- projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt
- projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
- projects/browser/allowed_addons.json
- projects/browser/config
- projects/firefox-android/config
- projects/firefox/build
- projects/firefox/config
- projects/geckoview/config
- projects/go/config
- projects/openssl/config
- projects/release/update_responses_config.yml
- projects/tor/build
- projects/tor/config
- projects/translation/config
- rbm.conf
Changes:
=====================================
projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt
=====================================
@@ -1,3 +1,37 @@
+Mullvad Browser 13.0a2 - August 08 2023
+ * All Platforms
+ * Updated NoScript to 11.4.26
+ * Upated uBlock Origin to 1.51.0
+ * Updated Firefox to 115.1.0esr
+ * Bug 195: Choose which locales to translate Mullvad Browser to [mullvad-browser]
+ * Bug 216: Rebase Mullvad Browser onto 115.1.0esr [mullvad-browser]
+ * Bug 30556: Re-evaluate letterboxing dimension choices [tor-browser]
+ * Bug 33282: Increase the max width of new windows [tor-browser]
+ * Bug 40916: Update updated_responses_config.yml to pull Mullvad incrementals from archive.torproject.org [tor-browser-build]
+ * Build System
+ * All Platforms
+ * Bug 198: Enable localization for Mullvad Browser builds [mullvad-browser]
+ * Bug 199: Mullvad Browser changes required to use Mullvad Browser-specific localization strings [mullvad-browser]
+ * Bug 40615: Consider adding a readme to the fonts directory [tor-browser-build]
+ * Bug 40880: The README doesn't include some dependencies needed for building incrementals [tor-browser-build]
+ * Bug 40907: Mar-tools aren't deterministic on 13.0a1 [tor-browser-build]
+ * Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects [tor-browser-build]
+ * Bug 40913: add boklm back to list of taggers in relevant projects [tor-browser-build]
+
+Mullvad Browser 12.5.2 - July 31 2023
+ * All Platforms
+ * Updated NoScript to 11.4.26
+ * Upated uBlock Origin to 1.51.0
+ * Updated Firefox to 102.14.0esr
+ * Bug 217: Rebase Mullvad Browser 12.5 stable on top of 102.14esr [mullvad-browser]
+ * Build System
+ * All Platforms
+ * Bug 40889: Add mullvad sha256sums URL to tools/signing/download-unsigned-sha256sums-gpg-signatures-from-people-tpo [tor-browser-build]
+ * Bug 40894: Fix format of keyring/boklm.gpg [tor-browser-build]
+ * Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects [tor-browser-build]
+ * Windows
+ * Bug 31546: Create and expose PDB files for Tor Browser debugging on Windows [tor-browser-build]
+
Mullvad Browser 13.0a1 - July 20 2023
* All Platforms
* Updated NoScript to 11.4.25
=====================================
projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
=====================================
@@ -1,3 +1,67 @@
+Tor Browser 13.0a2 - August 08 2023
+ * All Platforms
+ * Updated Translations
+ * Updated NoScript to 11.4.26
+ * Updated OpenSSL to 3.0.10
+ * Updated tor to 0.4.8.3-rc
+ * Bug 41909: Rebase 13.0 alpha to 115.1.0 esr [tor-browser]
+ * Windows + macOS + Linux
+ * Updated Firefox to 115.1.0esr
+ * Bug 30556: Re-evaluate letterboxing dimension choices [tor-browser]
+ * Bug 33282: Increase the max width of new windows [tor-browser]
+ * Bug 40982: Cleanup maps in tor-circuit-display [tor-browser]
+ * Bug 40983: Move not UI-related torbutton.js code to modules [tor-browser]
+ * Bug 41844: Stop using the control port directly [tor-browser]
+ * Bug 41907: The bootstrap is interrupted without any errors if the process becomes ready when already bootstrapping [tor-browser]
+ * Bug 41922: Unify the bridge line parsers [tor-browser]
+ * Bug 41923: The path normalization results in warnings [tor-browser]
+ * Bug 41924: Small refactors for TorProcess [tor-browser]
+ * Bug 41925: Remove the torbutton startup process [tor-browser]
+ * Bug 41926: Refactor the control port client implementation [tor-browser]
+ * Bug 41964: 'emojiAnnotations' not defined in time in connection preferences [tor-browser]
+ * Android
+ * Updated GeckoView to 115.1.0esr
+ * Bug 41928: Backport Android-specific security fixes from Firefox 116 to ESR 102.14 / 115.1 - based Tor Browser [tor-browser]
+ * Build System
+ * All Platforms
+ * Updated Go to 1.20.7
+ * Bug 31588: Be smarter about vendoring for Rust projects [tor-browser-build]
+ * Bug 40855: Update toolchains for Mozilla 115 [tor-browser-build]
+ * Bug 40880: The README doesn't include some dependencies needed for building incrementals [tor-browser-build]
+ * Bug 40905: Go vendor archives ignore the nightly version override on testbuilds [tor-browser-build]
+ * Bug 40908: Enable the --enable-gpl config flag in tor to bring in PoW functionality [tor-browser-build]
+ * Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects [tor-browser-build]
+ * Bug 40913: add boklm back to list of taggers in relevant projects [tor-browser-build]
+ * Windows + macOS + Linux
+ * Bug 40615: Consider adding a readme to the fonts directory [tor-browser-build]
+ * Bug 40907: Mar-tools aren't deterministic on 13.0a1 [tor-browser-build]
+ * Windows
+ * Bug 31546: Create and expose PDB files for Tor Browser debugging on Windows [tor-browser-build]
+ * Android
+ * Bug 40867: Create a RBM project for the unified Android repository [tor-browser-build]
+ * Bug 41899: Use LLD for Android [tor-browser]
+
+Tor Browser 12.5.2 - July 31 2023
+ * All Platforms
+ * Updated Translations
+ * Updated NoScript to 11.4.26
+ * Bug 41908: Rebase stable 12.5 to 102.14esr [tor-browser]
+ * Windows + macOS + Linux
+ * Updated Firefox to 102.14.0esr
+ * Windows
+ * Bug 41761: xul.dll win crash tor-browser 12.5.1 (based on Mozilla Firefox 102.13.0esr) (64-Bit) [tor-browser]
+ * Android
+ * Updated GeckoView to 102.14.0esr
+ * Bug 41928: Backport Android-specific security fixes from Firefox 116 to ESR 102.14 / 115.1 - based Tor Browser [tor-browser]
+ * Build System
+ * All Platforms
+ * Updated Go to 1.20.6
+ * Bug 40889: Add mullvad sha256sums URL to tools/signing/download-unsigned-sha256sums-gpg-signatures-from-people-tpo [tor-browser-build]
+ * Bug 40894: Fix format of keyring/boklm.gpg [tor-browser-build]
+ * Bug 40909: Add dan_b and ma1 to list of taggers in relevant projects [tor-browser-build]
+ * Windows
+ * Bug 31546: Create and expose PDB files for Tor Browser debugging on Windows [tor-browser-build]
+
Tor Browser 13.0a1 - July 20 2023
* All Platforms
* Updated Translations
=====================================
projects/browser/allowed_addons.json
=====================================
@@ -17,7 +17,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/34/9734/13299734/13299734.pn…"
}
],
- "average_daily_users": 962293,
+ "average_daily_users": 963152,
"categories": {
"android": [
"experimental",
@@ -221,10 +221,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.5599,
- "bayesian_average": 4.558742718876036,
- "count": 5081,
- "text_count": 1597
+ "average": 4.5592,
+ "bayesian_average": 4.55804261202379,
+ "count": 5093,
+ "text_count": 1604
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/reviews/",
"requires_payment": false,
@@ -321,7 +321,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/darkreader/versions/",
- "weekly_downloads": 23021
+ "weekly_downloads": 24659
},
"notes": null
},
@@ -337,7 +337,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/56/7656/6937656/6937656.png?…"
}
],
- "average_daily_users": 249664,
+ "average_daily_users": 249415,
"categories": {
"android": [
"security-privacy"
@@ -553,9 +553,9 @@
"category": "recommended"
},
"ratings": {
- "average": 4.8163,
- "bayesian_average": 4.811654069453744,
- "count": 1350,
+ "average": 4.816,
+ "bayesian_average": 4.811352368325867,
+ "count": 1353,
"text_count": 238
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/reviews/",
@@ -641,7 +641,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/decentraleyes/versions/",
- "weekly_downloads": 3297
+ "weekly_downloads": 3670
},
"notes": null
},
@@ -657,7 +657,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/73/4073/5474073/5474073.png?…"
}
],
- "average_daily_users": 1077131,
+ "average_daily_users": 1076698,
"categories": {
"android": [
"security-privacy"
@@ -1180,9 +1180,9 @@
"category": "recommended"
},
"ratings": {
- "average": 4.8004,
- "bayesian_average": 4.797633164122742,
- "count": 2259,
+ "average": 4.8009,
+ "bayesian_average": 4.798135100629341,
+ "count": 2265,
"text_count": 431
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/reviews/",
@@ -1207,7 +1207,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/versions/",
- "weekly_downloads": 17147
+ "weekly_downloads": 18718
},
"notes": null
},
@@ -1223,7 +1223,7 @@
"picture_url": null
}
],
- "average_daily_users": 6213889,
+ "average_daily_users": 6229783,
"categories": {
"android": [
"security-privacy"
@@ -1388,7 +1388,7 @@
},
"is_disabled": false,
"is_experimental": false,
- "last_updated": "2023-07-25T13:50:33Z",
+ "last_updated": "2023-08-07T17:15:41Z",
"name": {
"ar": "uBlock Origin",
"bg": "uBlock Origin",
@@ -1533,10 +1533,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.782,
- "bayesian_average": 4.781604256114891,
- "count": 15731,
- "text_count": 4091
+ "average": 4.7828,
+ "bayesian_average": 4.782404807048353,
+ "count": 15798,
+ "text_count": 4101
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/reviews/",
"requires_payment": false,
@@ -1598,7 +1598,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/versions/",
- "weekly_downloads": 129273
+ "weekly_downloads": 143905
},
"notes": null
},
@@ -1614,7 +1614,7 @@
"picture_url": null
}
],
- "average_daily_users": 167699,
+ "average_daily_users": 167376,
"categories": {
"android": [
"photos-media"
@@ -1713,10 +1713,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.4916,
- "bayesian_average": 4.486471885365025,
- "count": 1129,
- "text_count": 422
+ "average": 4.4929,
+ "bayesian_average": 4.487789225993283,
+ "count": 1132,
+ "text_count": 423
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/re…",
"requires_payment": false,
@@ -1738,7 +1738,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix/ve…",
- "weekly_downloads": 367
+ "weekly_downloads": 328
},
"notes": null
},
@@ -1754,7 +1754,7 @@
"picture_url": null
}
],
- "average_daily_users": 85522,
+ "average_daily_users": 85334,
"categories": {
"android": [
"experimental",
@@ -1892,7 +1892,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/privacy-possum/versions/",
- "weekly_downloads": 1659
+ "weekly_downloads": 1565
},
"notes": null
},
@@ -1908,7 +1908,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/64/9064/12929064/12929064.pn…"
}
],
- "average_daily_users": 257323,
+ "average_daily_users": 257984,
"categories": {
"android": [
"photos-media",
@@ -2127,10 +2127,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.652,
- "bayesian_average": 4.647356910324775,
- "count": 1296,
- "text_count": 249
+ "average": 4.6539,
+ "bayesian_average": 4.64926734511088,
+ "count": 1303,
+ "text_count": 250
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/reviews/",
"requires_payment": false,
@@ -2151,7 +2151,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/search_by_image/versions/",
- "weekly_downloads": 4065
+ "weekly_downloads": 4027
},
"notes": null
},
@@ -2174,7 +2174,7 @@
"picture_url": null
}
],
- "average_daily_users": 111166,
+ "average_daily_users": 111124,
"categories": {
"android": [
"other"
@@ -2457,10 +2457,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.4202,
- "bayesian_average": 4.41559222474328,
- "count": 1228,
- "text_count": 332
+ "average": 4.4123,
+ "bayesian_average": 4.407708861807205,
+ "count": 1232,
+ "text_count": 334
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/reviews/",
"requires_payment": false,
@@ -2480,7 +2480,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/google-search-fixer/versions/",
- "weekly_downloads": 33
+ "weekly_downloads": 30
},
"notes": null
},
@@ -2496,7 +2496,7 @@
"picture_url": "https://addons.mozilla.org/user-media/userpics/43/0143/143/143.png?modified…"
}
],
- "average_daily_users": 301822,
+ "average_daily_users": 301669,
"categories": {
"android": [
"performance",
@@ -2686,10 +2686,10 @@
"category": "recommended"
},
"ratings": {
- "average": 4.404,
- "bayesian_average": 4.401301959464707,
- "count": 2089,
- "text_count": 810
+ "average": 4.4047,
+ "bayesian_average": 4.402001757843669,
+ "count": 2093,
+ "text_count": 811
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/reviews/",
"requires_payment": false,
@@ -2733,7 +2733,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/noscript/versions/",
- "weekly_downloads": 7807
+ "weekly_downloads": 7783
},
"notes": null
},
@@ -2749,7 +2749,7 @@
"picture_url": null
}
],
- "average_daily_users": 149721,
+ "average_daily_users": 150191,
"categories": {
"android": [
"performance",
@@ -2864,9 +2864,9 @@
"category": "recommended"
},
"ratings": {
- "average": 3.9005,
- "bayesian_average": 3.89624887816371,
- "count": 1146,
+ "average": 3.9015,
+ "bayesian_average": 3.8972613162211953,
+ "count": 1147,
"text_count": 406
},
"ratings_url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/revi…",
@@ -2886,7 +2886,7 @@
"type": "extension",
"url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/",
"versions_url": "https://addons.mozilla.org/en-US/firefox/addon/youtube-high-definition/vers…",
- "weekly_downloads": 1670
+ "weekly_downloads": 1838
},
"notes": null
}
=====================================
projects/browser/config
=====================================
@@ -104,9 +104,9 @@ input_files:
- URL: https://addons.mozilla.org/firefox/downloads/file/4141345/noscript-11.4.26.…
name: noscript
sha256sum: 283db0eaebbd2888c1a852f5acabaa8e0225ff1eb1a97a25bceaedfd14d9f44c
- - URL: https://addons.mozilla.org/firefox/downloads/file/4121906/ublock_origin-1.5…
+ - URL: https://addons.mozilla.org/firefox/downloads/file/4141256/ublock_origin-1.5…
name: ublock-origin
- sha256sum: 10618003e70b528c3f17996e373146d39e6b15f777ac4ca1f214da2ffdb7a5b3
+ sha256sum: 8b73468bc233a11dd2895219466381783d19123857dd0b6fd16a01820fca4834
enable: '[% c("var/mullvad-browser") %]'
- URL: https://github.com/mullvad/browser-extension/releases/download/v0.8.3-firef…
name: mullvad-extension
=====================================
projects/firefox-android/config
=====================================
@@ -16,7 +16,7 @@ container:
var:
fenix_version: 115.2.0
browser_branch: 13.0-1
- browser_build: 1
+ browser_build: 2
platform_version: 115.0
variant: Beta
# This should be updated when the list of gradle dependencies is changed.
=====================================
projects/firefox/build
=====================================
@@ -159,7 +159,7 @@ mkdir "$HOME/.mozbuild"
pushd "$rootdir/translation-mullvad-browser"
ln -s ja ja-JP-mac
for lang in $supported_locales; do
- cp -r $lang "$l10ncentral/"
+ cp -Lr $lang "$l10ncentral/"
done
popd
[% END -%]
=====================================
projects/firefox/config
=====================================
@@ -14,11 +14,11 @@ container:
use_container: 1
var:
- firefox_platform_version: 115.0.2
+ firefox_platform_version: 115.1.0
firefox_version: '[% c("var/firefox_platform_version") %]esr'
browser_series: '13.0'
browser_branch: '[% c("var/browser_series") %]-1'
- browser_build: 3
+ browser_build: 4
branding_directory_prefix: 'tb'
copyright_year: '[% exec("git show -s --format=%ci").remove("-.*") %]'
nightly_updates_publish_dir: '[% c("var/nightly_updates_publish_dir_prefix") %]nightly-[% c("var/osname") %]'
@@ -85,7 +85,7 @@ targets:
git_url: https://gitlab.torproject.org/tpo/applications/mullvad-browser.git
var:
branding_directory_prefix: 'mb'
- browser_build: 2
+ browser_build: 3
gitlab_project: https://gitlab.torproject.org/tpo/applications/mullvad-browser
linux-x86_64:
=====================================
projects/geckoview/config
=====================================
@@ -16,7 +16,7 @@ container:
var:
geckoview_version: 115.1.0esr
browser_branch: 13.0-1
- browser_build: 2
+ browser_build: 4
copyright_year: '[% exec("git show -s --format=%ci").remove("-.*") %]'
gitlab_project: https://gitlab.torproject.org/tpo/applications/tor-browser
git_commit: '[% exec("git rev-parse HEAD") %]'
=====================================
projects/go/config
=====================================
@@ -1,5 +1,5 @@
# vim: filetype=yaml sw=2
-version: 1.20.6
+version: 1.20.7
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]'
container:
use_container: 1
@@ -119,7 +119,7 @@ input_files:
enable: '[% ! c("var/linux") %]'
- URL: 'https://golang.org/dl/go[% c("version") %].src.tar.gz'
name: go
- sha256sum: 62ee5bc6fb55b8bae8f705e0cb8df86d6453626b4ecf93279e2867092e0b7f70
+ sha256sum: 2c5ee9c9ec1e733b0dbbc2bdfed3f62306e51d8172bf38f4f4e542b27520f597
- project: go-bootstrap
name: go-bootstrap
target_replace:
=====================================
projects/openssl/config
=====================================
@@ -1,5 +1,5 @@
# vim: filetype=yaml sw=2
-version: 3.0.9
+version: 3.0.10
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]'
container:
use_container: 1
@@ -33,4 +33,4 @@ input_files:
- name: '[% c("var/compiler") %]'
project: '[% c("var/compiler") %]'
- URL: 'https://www.openssl.org/source/openssl-[% c("version") %].tar.gz'
- sha256sum: eb1ab04781474360f77c318ab89d8c5a03abc38e63d65a603cabbf1b00a1dc90
+ sha256sum: 1761d4f5b13a1028b9b6f3d4b8e17feb0cedc9370f6afe61d7193d2cdce83323
=====================================
projects/release/update_responses_config.yml
=====================================
@@ -10,13 +10,12 @@ appname_bundle_win64: '[% c("var/projectname") %]-install-win64'
releases_dir: [% path(c('output_dir')) %][% IF ! c("var/nightly") %]/signed[% END %]
download:
gpg_keyring: ../../keyring/torbrowser.gpg
+ archive_url: 'https://archive.torproject.org/tor-package-archive/[% c("var/projectname") %]'
[% IF c("var/tor-browser") -%]
- archive_url: 'https://archive.torproject.org/tor-package-archive/torbrowser'
bundles_url: 'https://dist.torproject.org/torbrowser'
mars_url: 'https://cdn.torproject.org/aus1/torbrowser'
[% END -%]
[% IF c("var/mullvad-browser") -%]
- archive_url: 'https://cdn.mullvad.net/browser'
bundles_url: 'https://cdn.mullvad.net/browser'
mars_url: 'https://cdn.mullvad.net/browser'
[% END -%]
=====================================
projects/tor/build
=====================================
@@ -73,7 +73,7 @@ find -type f -print0 | xargs -0 [% c("touch") %]
[% IF c("var/windows") || c("var/android") %]--with-zlib-dir="$zlibdir"[% END %] \
[% IF c("var/macos") %]--enable-static-openssl[% END %] \
[% IF c("var/windows") %]--enable-static-libevent --enable-static-openssl --enable-static-zlib[% END %] \
- --prefix="$distdir" [% c("var/configure_opt") %]
+ --enable-gpl --prefix="$distdir" [% c("var/configure_opt") %]
[% IF c("var/macos") -%]
export LD_PRELOAD=[% c("var/faketime_path") %]
export FAKETIME="[% USE date; GET date.format(c('timestamp'), format = '%Y-%m-%d %H:%M:%S') %]"
=====================================
projects/tor/config
=====================================
@@ -1,6 +1,6 @@
# vim: filetype=yaml sw=2
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]'
-version: 0.4.8.2-alpha
+version: 0.4.8.3-rc
git_hash: 'tor-[% c("version") %]'
git_url: https://gitlab.torproject.org/tpo/core/tor.git
git_submodule: 1
=====================================
projects/translation/config
=====================================
@@ -12,19 +12,19 @@ compress_tar: 'gz'
steps:
base-browser:
base-browser: '[% INCLUDE build %]'
- git_hash: 060d9d04b3a4c3e0f09f3f5fa8ad3ab12e2f4e80
+ git_hash: f940956c425c199f622f53fef9a735fc303b11f1
targets:
nightly:
git_hash: 'base-browser'
base-browser-fluent:
base-browser-fluent: '[% INCLUDE build %]'
- git_hash: 6f64004400616a5956d1823a0e8176b60d212090
+ git_hash: 72c0d47f55994b6d68e0239d9edd02d7ae7708ab
targets:
nightly:
git_hash: 'basebrowser-newidentityftl'
tor-browser:
tor-browser: '[% INCLUDE build %]'
- git_hash: d8d2b8f3200aa309f44f2fa80625ff8e6a25bb78
+ git_hash: 2b885b2a6dfeaa046678133755639d6e26485754
targets:
nightly:
git_hash: 'tor-browser'
=====================================
rbm.conf
=====================================
@@ -81,12 +81,10 @@ buildconf:
git_signtag_opt: '-s'
var:
- torbrowser_version: '13.0a1'
+ torbrowser_version: '13.0a2'
torbrowser_build: 'build1'
torbrowser_incremental_from:
- - 12.5a5
- - 12.5a6
- - 12.5a7
+ - '13.0a1'
updater_enabled: 1
build_mar: 1
mar_channel_id: '[% c("var/projectname") %]-torproject-[% c("var/channel") %]'
@@ -302,9 +300,6 @@ targets:
- tr
- zh-CN
- zh-TW
- torbrowser_build: 'build2'
- torbrowser_incremental_from:
- - 12.5a7
torbrowser-testbuild:
- testbuild
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/compare/…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/compare/…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-115.1.0esr-13.0-1-build4
by richard (@richard) 09 Aug '23
by richard (@richard) 09 Aug '23
09 Aug '23
richard pushed new tag tor-browser-115.1.0esr-13.0-1-build4 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/tor-brows…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] MB 198: Enable additional languages on Mullvad Browser.
by Pier Angelo Vendrame (@pierov) 08 Aug '23
by Pier Angelo Vendrame (@pierov) 08 Aug '23
08 Aug '23
Pier Angelo Vendrame pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
a93cb13f by Pier Angelo Vendrame at 2023-08-08T18:09:08+02:00
MB 198: Enable additional languages on Mullvad Browser.
Also MB 199: Inject Mullvad Browser-specific localized strings in
projects/firefox.
- - - - -
5 changed files:
- .gitlab/issue_templates/Release Prep - Mullvad Browser Alpha.md
- projects/firefox/build
- projects/firefox/config
- projects/translation/config
- rbm.conf
Changes:
=====================================
.gitlab/issue_templates/Release Prep - Mullvad Browser Alpha.md
=====================================
@@ -47,6 +47,7 @@ Mullvad Browser Alpha (and Nightly) are on the `main` branch
- [ ] run `make list_translation_updates-alpha` to get updated hashes
- [ ] `steps/base-browser/git_hash` : update with `HEAD` commit of project's `base-browser` branch
- [ ] `steps/base-browser-fluent/git_hash` : update with `HEAD` commit of project's `basebrowser-newidentityftl` branch
+ - [ ] `steps/mullvad-browser/git_hash` : update with `HEAD` commit of project's `mullvad-browser` branch
- [ ] Update common build configs
- [ ] Check for NoScript updates here : https://addons.mozilla.org/en-US/firefox/addon/noscript
- [ ] ***(Optional)*** If new version available, update `noscript` section of `input_files` in `projects/browser/config`
=====================================
projects/firefox/build
=====================================
@@ -154,6 +154,14 @@ mkdir "$HOME/.mozbuild"
echo "% locale torbutton $lang %locale/$lang/" >> "$torbutton_jar"
echo " locale/$lang (chrome/locale/$lang/*)" >> "$torbutton_jar"
done
+ [% ELSIF c("var/mullvad-browser") -%]
+ tar -C "$rootdir" -xf "$rootdir/[% c('input_files_by_name/translation-mullvad-browser') %]"
+ pushd "$rootdir/translation-mullvad-browser"
+ ln -s ja ja-JP-mac
+ for lang in $supported_locales; do
+ cp -r $lang "$l10ncentral/"
+ done
+ popd
[% END -%]
[% ELSE -%]
supported_locales=""
=====================================
projects/firefox/config
=====================================
@@ -199,6 +199,10 @@ input_files:
name: translation-tor-browser
pkg_type: tor-browser
enable: '[% c("var/tor-browser") && c("var/has_l10n") %]'
+ - project: translation
+ name: translation-mullvad-browser
+ pkg_type: mullvad-browser
+ enable: '[% c("var/mullvad-browser") && c("var/has_l10n") %]'
- filename: marsigner.der
enable: '[% c("var/override_updater_url") %]'
- filename: namecoin-torbutton.patch
=====================================
projects/translation/config
=====================================
@@ -28,6 +28,12 @@ steps:
targets:
nightly:
git_hash: 'tor-browser'
+ mullvad-browser:
+ mullvad-browser: '[% INCLUDE build %]'
+ git_hash: 1f736c5ae157f27df975c18bf3f2fa5f4bb5e33a
+ targets:
+ nightly:
+ git_hash: 'mullvad-browser'
fenix:
fenix: '[% INCLUDE build %]'
# We need to bump the commit before releasing but just pointing to a branch
@@ -40,7 +46,7 @@ steps:
list_updates:
list_updates: |
[%
- FOREACH component = [ 'base-browser', 'base-browser-fluent', 'tor-browser', 'fenix' ];
+ FOREACH component = [ 'base-browser', 'base-browser-fluent', 'tor-browser', 'mullvad-browser', 'fenix' ];
branch = pc(project, 'git_hash', { step => component, target => [ 'nightly' ] });
commit_hash = exec('git rev-parse ' _ branch, { git_hash => branch });
IF commit_hash == pc(project, "git_hash", { step => component });
=====================================
rbm.conf
=====================================
@@ -280,7 +280,28 @@ targets:
ProjectName: MullvadBrowser
exe_name: mullvadbrowser
mar_channel_id: '[% c("var/projectname") %]-mullvad-[% c("var/channel") %]'
- locales: []
+ locales:
+ - ar
+ - da
+ - de
+ - es-ES
+ - fa
+ - fi
+ - fr
+ - it
+ - '[% c("var/locale_ja") %]'
+ - ko
+ - my
+ - nb-NO
+ - nl
+ - pl
+ - pt-BR
+ - ru
+ - sv-SE
+ - th
+ - tr
+ - zh-CN
+ - zh-TW
torbrowser_build: 'build2'
torbrowser_incremental_from:
- 12.5a7
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/a…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/a…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][base-browser-115.1.0esr-13.0-1] 2 commits: amend! Bug 32308: use direct browser sizing for letterboxing.
by ma1 (@ma1) 08 Aug '23
by ma1 (@ma1) 08 Aug '23
08 Aug '23
ma1 pushed to branch base-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
dc2274d0 by hackademix at 2023-08-08T09:18:01+02:00
amend! Bug 32308: use direct browser sizing for letterboxing.
Bug 32308: use direct browser sizing for letterboxing.
Bug 30556: align letterboxing with 200x100 new win width stepping
- - - - -
9ac98dab by hackademix at 2023-08-08T09:18:02+02:00
fixup! Firefox preference overrides.
Bug 33282: Redefine the dimensions of new RFP windows
- - - - -
2 changed files:
- browser/app/profile/001-base-profile.js
- toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -291,6 +291,9 @@ pref("dom.use_components_shim", false);
pref("privacy.resistFingerprinting.letterboxing", true);
// tor-browser#41695: how many warnings we show if user closes them without restoring the window size
pref("privacy.resistFingerprinting.resizeWarnings", 3);
+// tor-browser#33282: new windows start at 1400x900 when there's enough screen space, otherwise down by 200x100 blocks
+pref("privacy.window.maxInnerWidth", 1400);
+pref("privacy.window.maxInnerHeight", 900);
// Enforce Network Information API as disabled
pref("dom.netinfo.enabled", false);
pref("network.http.referer.defaultPolicy", 2); // Bug 32948: Make referer behavior consistent regardless of private browing mode status
=====================================
toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
=====================================
@@ -475,14 +475,14 @@ class _RFPHelper {
/**
* Given a width or height, rounds it with the proper stepping.
*/
- steppedSize(aDimension) {
+ steppedSize(aDimension, isWidth = false) {
let stepping;
if (aDimension <= 50) {
return 0;
} else if (aDimension <= 500) {
stepping = 50;
} else if (aDimension <= 1600) {
- stepping = 100;
+ stepping = isWidth ? 200 : 100;
} else {
stepping = 200;
}
@@ -569,7 +569,7 @@ class _RFPHelper {
// If the set is empty, we will round the content with the default
// stepping size.
if (!this._letterboxingDimensions.length) {
- result = r(this.steppedSize(aWidth), this.steppedSize(aHeight));
+ result = r(this.steppedSize(aWidth, true), this.steppedSize(aHeight));
log(
`${logPrefix} roundDimensions(${aWidth}, ${aHeight}) = ${result.width} x ${result.height}`
);
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/65183e…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/65183e…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] fixup! Firefox preference overrides.
by ma1 (@ma1) 08 Aug '23
by ma1 (@ma1) 08 Aug '23
08 Aug '23
ma1 pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
e0e763c0 by hackademix at 2023-08-08T09:11:30+02:00
fixup! Firefox preference overrides.
Bug 33282: Redefine the dimensions of new RFP windows
- - - - -
1 changed file:
- browser/app/profile/001-base-profile.js
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -291,6 +291,9 @@ pref("dom.use_components_shim", false);
pref("privacy.resistFingerprinting.letterboxing", true);
// tor-browser#41695: how many warnings we show if user closes them without restoring the window size
pref("privacy.resistFingerprinting.resizeWarnings", 3);
+// tor-browser#33282: new windows start at 1400x900 when there's enough screen space, otherwise down by 200x100 blocks
+pref("privacy.window.maxInnerWidth", 1400);
+pref("privacy.window.maxInnerHeight", 900);
// Enforce Network Information API as disabled
pref("dom.netinfo.enabled", false);
pref("network.http.referer.defaultPolicy", 2); // Bug 32948: Make referer behavior consistent regardless of private browing mode status
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/e0e763c…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/e0e763c…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] amend! Bug 32308: use direct browser sizing for letterboxing.
by ma1 (@ma1) 08 Aug '23
by ma1 (@ma1) 08 Aug '23
08 Aug '23
ma1 pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
63eecb92 by hackademix at 2023-08-08T08:08:43+02:00
amend! Bug 32308: use direct browser sizing for letterboxing.
Bug 32308: use direct browser sizing for letterboxing.
Bug 30556: align letterboxing with 200x100 new win width stepping
- - - - -
1 changed file:
- toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
Changes:
=====================================
toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
=====================================
@@ -475,14 +475,14 @@ class _RFPHelper {
/**
* Given a width or height, rounds it with the proper stepping.
*/
- steppedSize(aDimension) {
+ steppedSize(aDimension, isWidth = false) {
let stepping;
if (aDimension <= 50) {
return 0;
} else if (aDimension <= 500) {
stepping = 50;
} else if (aDimension <= 1600) {
- stepping = 100;
+ stepping = isWidth ? 200 : 100;
} else {
stepping = 200;
}
@@ -569,7 +569,7 @@ class _RFPHelper {
// If the set is empty, we will round the content with the default
// stepping size.
if (!this._letterboxingDimensions.length) {
- result = r(this.steppedSize(aWidth), this.steppedSize(aHeight));
+ result = r(this.steppedSize(aWidth, true), this.steppedSize(aHeight));
log(
`${logPrefix} roundDimensions(${aWidth}, ${aHeight}) = ${result.width} x ${result.height}`
);
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/63eecb9…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/63eecb9…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-115.1.0esr-13.0-1-build3
by richard (@richard) 07 Aug '23
by richard (@richard) 07 Aug '23
07 Aug '23
richard pushed new tag tor-browser-115.1.0esr-13.0-1-build3 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/tor-brows…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] 8 commits: fixup! Bug 40933: Add tor-launcher functionality
by richard (@richard) 07 Aug '23
by richard (@richard) 07 Aug '23
07 Aug '23
richard pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
d339153b by Pier Angelo Vendrame at 2023-08-07T18:36:05+02:00
fixup! Bug 40933: Add tor-launcher functionality
Use the new functions whenever possible, adjust some property names and
other minor fixes.
- - - - -
e8d3c0b8 by Pier Angelo Vendrame at 2023-08-07T18:36:14+02:00
fixup! Bug 40597: Implement TorSettings module
Changes needed for the new control port implementation.
Also, moved to ES modules and done some refactors on Moat.
- - - - -
c1b05a65 by Pier Angelo Vendrame at 2023-08-07T18:36:15+02:00
fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
Changes for the new control port implementation and following
ESMification.
- - - - -
d23cb300 by Pier Angelo Vendrame at 2023-08-07T18:36:15+02:00
fixup! Bug 30237: Add v3 onion services client authentication prompt
Changes for the new control port implementation.
- - - - -
c801d620 by Pier Angelo Vendrame at 2023-08-07T18:36:16+02:00
fixup! Bug 7494: Create local home page for TBB.
Use the TorProvider in TorCheckService
- - - - -
fa105e4f by Pier Angelo Vendrame at 2023-08-07T18:36:16+02:00
fixup! Bug 3455: Add DomainIsolator, for isolating circuit by domain.
Remove TorMonitorService and TorProtocolService references.
- - - - -
cd92f30e by Pier Angelo Vendrame at 2023-08-07T18:36:17+02:00
fixup! Bug 41668: Tweaks to the Base Browser updater for Tor Browser
Removed TorMonitorService reference
- - - - -
384dff97 by Pier Angelo Vendrame at 2023-08-07T18:36:17+02:00
fixup! Bug 40933: Add tor-launcher functionality
Remove the final references to TorMonitorService and TorProtocolService.
- - - - -
24 changed files:
- browser/components/abouttor/TorCheckService.sys.mjs
- browser/components/onionservices/content/authPrompt.js
- browser/components/onionservices/content/savedKeysDialog.js
- browser/components/torpreferences/content/builtinBridgeDialog.jsm
- browser/components/torpreferences/content/connectionPane.js
- browser/components/torpreferences/content/connectionSettingsDialog.jsm
- browser/components/torpreferences/content/provideBridgeDialog.jsm
- browser/components/torpreferences/content/requestBridgeDialog.jsm
- browser/components/torpreferences/content/torLogDialog.jsm
- browser/modules/BridgeDB.jsm → browser/modules/BridgeDB.sys.mjs
- browser/modules/Moat.jsm → browser/modules/Moat.sys.mjs
- browser/modules/TorConnect.jsm → browser/modules/TorConnect.sys.mjs
- browser/modules/TorSettings.jsm → browser/modules/TorSettings.sys.mjs
- browser/modules/moz.build
- toolkit/components/tor-launcher/TorBootstrapRequest.sys.mjs
- toolkit/components/tor-launcher/TorControlPort.sys.mjs
- toolkit/components/tor-launcher/TorDomainIsolator.sys.mjs
- − toolkit/components/tor-launcher/TorMonitorService.sys.mjs
- toolkit/components/tor-launcher/TorParsers.sys.mjs
- toolkit/components/tor-launcher/TorProtocolService.sys.mjs → toolkit/components/tor-launcher/TorProvider.sys.mjs
- toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
- toolkit/components/tor-launcher/TorStartupService.sys.mjs
- toolkit/components/tor-launcher/moz.build
- toolkit/mozapps/update/UpdateService.sys.mjs
Changes:
=====================================
browser/components/abouttor/TorCheckService.sys.mjs
=====================================
@@ -11,14 +11,9 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ConsoleAPI: "resource://gre/modules/Console.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
});
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorProtocolService",
- "resource://gre/modules/TorProtocolService.jsm"
-);
-
export const TorCheckService = {
kCheckNotInitiated: 0, // Possible values for status.
kCheckSuccessful: 1,
@@ -109,7 +104,7 @@ export const TorCheckService = {
let listeners;
try {
- listeners = await lazy.TorProtocolService.getSocksListeners();
+ listeners = await lazy.TorProviderBuilder.build().getSocksListeners();
} catch (e) {
this._logger.error("Failed to get the SOCKS listerner addresses.", e);
return false;
=====================================
browser/components/onionservices/content/authPrompt.js
=====================================
@@ -4,10 +4,13 @@
/* globals gBrowser, PopupNotifications, Services, XPCOMUtils */
+ChromeUtils.defineESModuleGetters(this, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
+
XPCOMUtils.defineLazyModuleGetters(this, {
OnionAuthUtil: "chrome://browser/content/onionservices/authUtil.jsm",
CommonUtils: "resource://services-common/utils.js",
- TorProtocolService: "resource://gre/modules/TorProtocolService.jsm",
TorStrings: "resource:///modules/TorStrings.jsm",
});
@@ -203,7 +206,8 @@ const OnionAuthPrompt = (function () {
let checkboxElem = this._getCheckboxElement();
let isPermanent = checkboxElem && checkboxElem.checked;
- TorProtocolService.onionAuthAdd(onionServiceId, base64key, isPermanent)
+ TorProviderBuilder.build()
+ .onionAuthAdd(onionServiceId, base64key, isPermanent)
.then(aResponse => {
// Success! Reload the page.
this._browser.sendMessageToActor(
=====================================
browser/components/onionservices/content/savedKeysDialog.js
=====================================
@@ -8,11 +8,9 @@ ChromeUtils.defineModuleGetter(
"resource:///modules/TorStrings.jsm"
);
-ChromeUtils.defineModuleGetter(
- this,
- "TorProtocolService",
- "resource://gre/modules/TorProtocolService.jsm"
-);
+ChromeUtils.defineESModuleGetters(this, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
var gOnionServicesSavedKeysDialog = {
selector: {
@@ -54,6 +52,7 @@ var gOnionServicesSavedKeysDialog = {
await this._deleteOneKey(indexesToDelete[i]);
}
} catch (e) {
+ console.error("Removing a saved key failed", e);
if (e.torMessage) {
this._showError(e.torMessage);
} else {
@@ -125,22 +124,16 @@ var gOnionServicesSavedKeysDialog = {
try {
this._tree.view = this;
- const keyInfoList = await TorProtocolService.onionAuthViewKeys();
+ const keyInfoList = await TorProviderBuilder.build().onionAuthViewKeys();
if (keyInfoList) {
// Filter out temporary keys.
- this._keyInfoList = keyInfoList.filter(aKeyInfo => {
- if (!aKeyInfo.Flags) {
- return false;
- }
-
- const flags = aKeyInfo.Flags.split(",");
- return flags.includes("Permanent");
- });
-
+ this._keyInfoList = keyInfoList.filter(aKeyInfo =>
+ aKeyInfo.flags?.includes("Permanent")
+ );
// Sort by the .onion address.
this._keyInfoList.sort((aObj1, aObj2) => {
- const hsAddr1 = aObj1.hsAddress.toLowerCase();
- const hsAddr2 = aObj2.hsAddress.toLowerCase();
+ const hsAddr1 = aObj1.address.toLowerCase();
+ const hsAddr2 = aObj2.address.toLowerCase();
if (hsAddr1 < hsAddr2) {
return -1;
}
@@ -164,7 +157,7 @@ var gOnionServicesSavedKeysDialog = {
// This method may throw; callers should catch errors.
async _deleteOneKey(aIndex) {
const keyInfoObj = this._keyInfoList[aIndex];
- await TorProtocolService.onionAuthRemove(keyInfoObj.hsAddress);
+ await TorProviderBuilder.build().onionAuthRemove(keyInfoObj.address);
this._tree.view.selection.clearRange(aIndex, aIndex);
this._keyInfoList.splice(aIndex, 1);
this._tree.rowCountChanged(aIndex + 1, -1);
@@ -193,26 +186,20 @@ var gOnionServicesSavedKeysDialog = {
// XUL tree widget view implementation.
get rowCount() {
- return this._keyInfoList ? this._keyInfoList.length : 0;
+ return this._keyInfoList?.length ?? 0;
},
getCellText(aRow, aCol) {
- let val = "";
if (this._keyInfoList && aRow < this._keyInfoList.length) {
const keyInfo = this._keyInfoList[aRow];
if (aCol.id.endsWith("-siteCol")) {
- val = keyInfo.hsAddress;
+ return keyInfo.address;
} else if (aCol.id.endsWith("-keyCol")) {
- val = keyInfo.typeAndKey;
- // Omit keyType because it is always "x25519".
- const idx = val.indexOf(":");
- if (idx > 0) {
- val = val.substring(idx + 1);
- }
+ // keyType is always "x25519", so do not show it.
+ return keyInfo.keyBlob;
}
}
-
- return val;
+ return "";
},
isSeparator(index) {
=====================================
browser/components/torpreferences/content/builtinBridgeDialog.jsm
=====================================
@@ -7,10 +7,10 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
const { TorSettings, TorBridgeSource, TorBuiltinBridgeTypes } =
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
+ ChromeUtils.importESModule("resource:///modules/TorSettings.sys.mjs");
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
- "resource:///modules/TorConnect.jsm"
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
+ "resource:///modules/TorConnect.sys.mjs"
);
class BuiltinBridgeDialog {
=====================================
browser/components/torpreferences/content/connectionPane.js
=====================================
@@ -12,20 +12,17 @@ const { setTimeout, clearTimeout } = ChromeUtils.import(
);
const { TorSettings, TorSettingsTopics, TorSettingsData, TorBridgeSource } =
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
+ ChromeUtils.importESModule("resource:///modules/TorSettings.sys.mjs");
const { TorParsers } = ChromeUtils.importESModule(
"resource://gre/modules/TorParsers.sys.mjs"
);
-const { TorProtocolService } = ChromeUtils.importESModule(
- "resource://gre/modules/TorProtocolService.sys.mjs"
-);
-const { TorMonitorService, TorMonitorTopics } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
+const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule(
+ "resource://gre/modules/TorProviderBuilder.sys.mjs"
);
const { TorConnect, TorConnectTopics, TorConnectState, TorCensorshipLevel } =
- ChromeUtils.import("resource:///modules/TorConnect.jsm");
+ ChromeUtils.importESModule("resource:///modules/TorConnect.sys.mjs");
const { TorLogDialog } = ChromeUtils.import(
"chrome://browser/content/torpreferences/torLogDialog.jsm"
@@ -51,7 +48,9 @@ const { ProvideBridgeDialog } = ChromeUtils.import(
"chrome://browser/content/torpreferences/provideBridgeDialog.jsm"
);
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
+const { MoatRPC } = ChromeUtils.importESModule(
+ "resource:///modules/Moat.sys.mjs"
+);
const { QRCode } = ChromeUtils.import("resource://gre/modules/QRCode.jsm");
@@ -156,7 +155,7 @@ const gConnectionPane = (function () {
_populateXUL() {
// saves tor settings to disk when navigate away from about:preferences
window.addEventListener("blur", val => {
- TorProtocolService.flushSettings();
+ TorProviderBuilder.build().flushSettings();
});
document
@@ -751,7 +750,7 @@ const gConnectionPane = (function () {
// TODO: We could make sure TorSettings is in sync by monitoring also
// changes of settings. At that point, we could query it, instead of
// doing a query over the control port.
- const bridge = TorMonitorService.currentBridge;
+ const bridge = TorProviderBuilder.build().currentBridge;
if (bridge?.fingerprint !== this._currentBridgeId) {
this._currentBridgeId = bridge?.fingerprint ?? null;
this._updateConnectedBridges();
@@ -850,7 +849,7 @@ const gConnectionPane = (function () {
});
Services.obs.addObserver(this, TorConnectTopics.StateChange);
- Services.obs.addObserver(this, TorMonitorTopics.BridgeChanged);
+ Services.obs.addObserver(this, TorProviderTopics.BridgeChanged);
Services.obs.addObserver(this, "intl:app-locales-changed");
},
@@ -875,7 +874,7 @@ const gConnectionPane = (function () {
// unregister our observer topics
Services.obs.removeObserver(this, TorSettingsTopics.SettingChanged);
Services.obs.removeObserver(this, TorConnectTopics.StateChange);
- Services.obs.removeObserver(this, TorMonitorTopics.BridgeChanged);
+ Services.obs.removeObserver(this, TorProviderTopics.BridgeChanged);
Services.obs.removeObserver(this, "intl:app-locales-changed");
},
@@ -907,7 +906,7 @@ const gConnectionPane = (function () {
this.onStateChange();
break;
}
- case TorMonitorTopics.BridgeChanged: {
+ case TorProviderTopics.BridgeChanged: {
if (data?.fingerprint !== this._currentBridgeId) {
this._checkConnectedBridge();
}
=====================================
browser/components/torpreferences/content/connectionSettingsDialog.jsm
=====================================
@@ -2,8 +2,8 @@
var EXPORTED_SYMBOLS = ["ConnectionSettingsDialog"];
-const { TorSettings, TorProxyType } = ChromeUtils.import(
- "resource:///modules/TorSettings.jsm"
+const { TorSettings, TorProxyType } = ChromeUtils.importESModule(
+ "resource:///modules/TorSettings.sys.mjs"
);
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
=====================================
browser/components/torpreferences/content/provideBridgeDialog.jsm
=====================================
@@ -6,12 +6,12 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
-const { TorSettings, TorBridgeSource } = ChromeUtils.import(
- "resource:///modules/TorSettings.jsm"
+const { TorSettings, TorBridgeSource } = ChromeUtils.importESModule(
+ "resource:///modules/TorSettings.sys.mjs"
);
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
- "resource:///modules/TorConnect.jsm"
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
+ "resource:///modules/TorConnect.sys.mjs"
);
class ProvideBridgeDialog {
=====================================
browser/components/torpreferences/content/requestBridgeDialog.jsm
=====================================
@@ -4,11 +4,13 @@ var EXPORTED_SYMBOLS = ["RequestBridgeDialog"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-const { BridgeDB } = ChromeUtils.import("resource:///modules/BridgeDB.jsm");
+const { BridgeDB } = ChromeUtils.importESModule(
+ "resource:///modules/BridgeDB.sys.mjs"
+);
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
- "resource:///modules/TorConnect.jsm"
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
+ "resource:///modules/TorConnect.sys.mjs"
);
class RequestBridgeDialog {
=====================================
browser/components/torpreferences/content/torLogDialog.jsm
=====================================
@@ -2,12 +2,12 @@
var EXPORTED_SYMBOLS = ["TorLogDialog"];
-const { setTimeout, clearTimeout } = ChromeUtils.import(
- "resource://gre/modules/Timer.jsm"
+const { setTimeout, clearTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
);
-const { TorMonitorService } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
+const { TorProviderBuilder } = ChromeUtils.importESModule(
+ "resource://gre/modules/TorProviderBuilder.sys.mjs"
);
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
@@ -56,7 +56,7 @@ class TorLogDialog {
}, RESTORE_TIME);
});
- this._logTextarea.value = TorMonitorService.getLog();
+ this._logTextarea.value = TorProviderBuilder.build().getLog();
}
init(window, aDialog) {
=====================================
browser/modules/BridgeDB.jsm → browser/modules/BridgeDB.sys.mjs
=====================================
@@ -1,10 +1,14 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = ["BridgeDB"];
+const lazy = {};
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
+ChromeUtils.defineESModuleGetters(lazy, {
+ MoatRPC: "resource:///modules/Moat.sys.mjs",
+});
-var BridgeDB = {
+export var BridgeDB = {
_moatRPC: null,
_challenge: null,
_image: null,
@@ -20,7 +24,7 @@ var BridgeDB = {
async submitCaptchaGuess(solution) {
if (!this._moatRPC) {
- this._moatRPC = new MoatRPC();
+ this._moatRPC = new lazy.MoatRPC();
await this._moatRPC.init();
}
@@ -37,7 +41,7 @@ var BridgeDB = {
async requestNewCaptchaImage() {
try {
if (!this._moatRPC) {
- this._moatRPC = new MoatRPC();
+ this._moatRPC = new lazy.MoatRPC();
await this._moatRPC.init();
}
=====================================
browser/modules/Moat.jsm → browser/modules/Moat.sys.mjs
=====================================
@@ -1,24 +1,19 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = ["MoatRPC"];
+import {
+ TorSettings,
+ TorBridgeSource,
+} from "resource:///modules/TorSettings.sys.mjs";
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const lazy = {};
-const { Subprocess } = ChromeUtils.import(
- "resource://gre/modules/Subprocess.jsm"
-);
-
-const { TorLauncherUtil } = ChromeUtils.import(
- "resource://gre/modules/TorLauncherUtil.jsm"
-);
-
-const { TorProtocolService } = ChromeUtils.import(
- "resource://gre/modules/TorProtocolService.jsm"
-);
-
-const { TorSettings, TorBridgeSource } = ChromeUtils.import(
- "resource:///modules/TorSettings.jsm"
-);
+ChromeUtils.defineESModuleGetters(lazy, {
+ Subprocess: "resource://gre/modules/Subprocess.sys.mjs",
+ TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
const TorLauncherPrefs = Object.freeze({
bridgedb_front: "extensions.torlauncher.bridgedb_front",
@@ -26,73 +21,54 @@ const TorLauncherPrefs = Object.freeze({
moat_service: "extensions.torlauncher.moat_service",
});
-// Config keys used to query tor daemon properties
-const TorConfigKeys = Object.freeze({
- clientTransportPlugin: "ClientTransportPlugin",
-});
-
//
// Launches and controls the PT process lifetime
//
class MeekTransport {
- constructor() {
- this._inited = false;
- this._meekClientProcess = null;
- this._meekProxyType = null;
- this._meekProxyAddress = null;
- this._meekProxyPort = 0;
- this._meekProxyUsername = null;
- this._meekProxyPassword = null;
- }
+ // These members are used by consumers to setup the proxy to do requests over
+ // meek. They are passed to newProxyInfoWithAuth.
+ proxyType = null;
+ proxyAddress = null;
+ proxyPort = 0;
+ proxyUsername = null;
+ proxyPassword = null;
+
+ #inited = false;
+ #meekClientProcess = null;
// launches the meekprocess
async init() {
// ensure we haven't already init'd
- if (this._inited) {
+ if (this.#inited) {
throw new Error("MeekTransport: Already initialized");
}
- // cleanup function for killing orphaned pt process
- let onException = () => {};
try {
// figure out which pluggable transport to use
const supportedTransports = ["meek", "meek_lite"];
- let transportPlugins = await TorProtocolService.readStringArraySetting(
- TorConfigKeys.clientTransportPlugin
+ const proxy = (
+ await lazy.TorProviderBuilder.build().getPluggableTransports()
+ ).find(
+ pt =>
+ pt.type === "exec" &&
+ supportedTransports.some(t => pt.transports.includes(t))
);
+ if (!proxy) {
+ throw new Error("No supported transport found.");
+ }
- let { meekTransport, meekClientPath, meekClientArgs } = (() => {
- for (const line of transportPlugins) {
- let tokens = line.split(" ");
- if (tokens.length > 2 && tokens[1] == "exec") {
- let transportArray = tokens[0].split(",").map(aStr => aStr.trim());
- let transport = transportArray.find(aTransport =>
- supportedTransports.includes(aTransport)
- );
-
- if (transport != undefined) {
- return {
- meekTransport: transport,
- meekClientPath: tokens[2],
- meekClientArgs: tokens.slice(3),
- };
- }
- }
- }
-
- return {
- meekTransport: null,
- meekClientPath: null,
- meekClientArgs: null,
- };
- })();
-
+ const meekTransport = proxy.transports.find(t =>
+ supportedTransports.includes(t)
+ );
// Convert meek client path to absolute path if necessary
- let meekWorkDir = TorLauncherUtil.getTorFile("pt-startup-dir", false);
- if (TorLauncherUtil.isPathRelative(meekClientPath)) {
- let meekPath = meekWorkDir.clone();
- meekPath.appendRelativePath(meekClientPath);
- meekClientPath = meekPath.path;
+ const meekWorkDir = lazy.TorLauncherUtil.getTorFile(
+ "pt-startup-dir",
+ false
+ );
+ if (lazy.TorLauncherUtil.isPathRelative(proxy.pathToBinary)) {
+ const meekPath = meekWorkDir.clone();
+ meekPath.appendRelativePath(proxy.pathToBinary);
+ proxy.pathToBinary = meekPath.path;
}
// Construct the per-connection arguments.
@@ -105,16 +81,13 @@ class MeekTransport {
// First the "<Key>=<Value>" formatted arguments MUST be escaped,
// such that all backslash, equal sign, and semicolon characters
// are escaped with a backslash.
- let escapeArgValue = aValue => {
- if (!aValue) {
- return "";
- }
-
- let rv = aValue.replace(/\\/g, "\\\\");
- rv = rv.replace(/=/g, "\\=");
- rv = rv.replace(/;/g, "\\;");
- return rv;
- };
+ const escapeArgValue = aValue =>
+ aValue
+ ? aValue
+ .replaceAll("\\", "\\\\")
+ .replaceAll("=", "\\=")
+ .replaceAll(";", "\\;")
+ : "";
if (meekReflector) {
meekClientEscapedArgs += "url=";
@@ -132,10 +105,10 @@ class MeekTransport {
}
// Setup env and start meek process
- let ptStateDir = TorLauncherUtil.getTorFile("tordatadir", false);
+ const ptStateDir = lazy.TorLauncherUtil.getTorFile("tordatadir", false);
ptStateDir.append("pt_state"); // Match what tor uses.
- let envAdditions = {
+ const envAdditions = {
TOR_PT_MANAGED_TRANSPORT_VER: "1",
TOR_PT_STATE_LOCATION: ptStateDir.path,
TOR_PT_EXIT_ON_STDIN_CLOSE: "1",
@@ -145,9 +118,9 @@ class MeekTransport {
envAdditions.TOR_PT_PROXY = TorSettings.proxy.uri;
}
- let opts = {
- command: meekClientPath,
- arguments: meekClientArgs,
+ const opts = {
+ command: proxy.pathToBinary,
+ arguments: proxy.options.split(/s+/),
workdir: meekWorkDir.path,
environmentAppend: true,
environment: envAdditions,
@@ -155,27 +128,23 @@ class MeekTransport {
};
// Launch meek client
- let meekClientProcess = await Subprocess.call(opts);
- // kill our process if exception is thrown
- onException = () => {
- meekClientProcess.kill();
- };
+ this.#meekClientProcess = await lazy.Subprocess.call(opts);
// Callback chain for reading stderr
- let stderrLogger = async () => {
- if (this._meekClientProcess) {
- let errString = await this._meekClientProcess.stderr.readString();
- console.log(`MeekTransport: stderr => ${errString}`);
- await stderrLogger();
+ const stderrLogger = async () => {
+ while (this.#meekClientProcess) {
+ const errString = await this.#meekClientProcess.stderr.readString();
+ if (errString) {
+ console.log(`MeekTransport: stderr => ${errString}`);
+ }
}
};
stderrLogger();
// Read pt's stdout until terminal (CMETHODS DONE) is reached
// returns array of lines for parsing
- let getInitLines = async (stdout = "") => {
- let string = await meekClientProcess.stdout.readString();
- stdout += string;
+ const getInitLines = async (stdout = "") => {
+ stdout += await this.#meekClientProcess.stdout.readString();
// look for the final message
const CMETHODS_DONE = "CMETHODS DONE";
@@ -188,20 +157,16 @@ class MeekTransport {
};
// read our lines from pt's stdout
- let meekInitLines = await getInitLines();
+ const meekInitLines = await getInitLines();
// tokenize our pt lines
- let meekInitTokens = meekInitLines.map(line => {
- let tokens = line.split(" ");
+ const meekInitTokens = meekInitLines.map(line => {
+ const tokens = line.split(" ");
return {
keyword: tokens[0],
args: tokens.slice(1),
};
});
- let meekProxyType = null;
- let meekProxyAddr = null;
- let meekProxyPort = 0;
-
// parse our pt tokens
for (const { keyword, args } of meekInitTokens) {
const argsJoined = args.join(" ");
@@ -251,9 +216,9 @@ class MeekTransport {
}
// convert proxy type to strings used by protocol-proxy-servce
- meekProxyType = proxyType === "socks5" ? "socks" : "socks4";
- meekProxyAddr = addr;
- meekProxyPort = port;
+ this.proxyType = proxyType === "socks5" ? "socks" : "socks4";
+ this.proxyAddress = addr;
+ this.proxyPort = port;
break;
}
@@ -278,49 +243,47 @@ class MeekTransport {
}
}
- this._meekClientProcess = meekClientProcess;
// register callback to cleanup on process exit
- this._meekClientProcess.wait().then(exitObj => {
- this._meekClientProcess = null;
+ this.#meekClientProcess.wait().then(exitObj => {
+ this.#meekClientProcess = null;
this.uninit();
});
- this._meekProxyType = meekProxyType;
- this._meekProxyAddress = meekProxyAddr;
- this._meekProxyPort = meekProxyPort;
-
// socks5
- if (meekProxyType === "socks") {
+ if (this.proxyType === "socks") {
if (meekClientEscapedArgs.length <= 255) {
- this._meekProxyUsername = meekClientEscapedArgs;
- this._meekProxyPassword = "\x00";
+ this.proxyUsername = meekClientEscapedArgs;
+ this.proxyPassword = "\x00";
} else {
- this._meekProxyUsername = meekClientEscapedArgs.substring(0, 255);
- this._meekProxyPassword = meekClientEscapedArgs.substring(255);
+ this.proxyUsername = meekClientEscapedArgs.substring(0, 255);
+ this.proxyPassword = meekClientEscapedArgs.substring(255);
}
// socks4
} else {
- this._meekProxyUsername = meekClientEscapedArgs;
- this._meekProxyPassword = undefined;
+ this.proxyUsername = meekClientEscapedArgs;
+ this.proxyPassword = undefined;
}
- this._inited = true;
+ this.#inited = true;
} catch (ex) {
- onException();
+ if (this.#meekClientProcess) {
+ this.#meekClientProcess.kill();
+ this.#meekClientProcess = null;
+ }
throw ex;
}
}
async uninit() {
- this._inited = false;
-
- await this._meekClientProcess?.kill();
- this._meekClientProcess = null;
- this._meekProxyType = null;
- this._meekProxyAddress = null;
- this._meekProxyPort = 0;
- this._meekProxyUsername = null;
- this._meekProxyPassword = null;
+ this.#inited = false;
+
+ await this.#meekClientProcess?.kill();
+ this.#meekClientProcess = null;
+ this.proxyType = null;
+ this.proxyAddress = null;
+ this.proxyPort = 0;
+ this.proxyUsername = null;
+ this.proxyPassword = null;
}
}
@@ -328,21 +291,25 @@ class MeekTransport {
// Callback object with a cached promise for the returned Moat data
//
class MoatResponseListener {
+ #response = "";
+ #responsePromise;
+ #resolve;
+ #reject;
constructor() {
- this._response = "";
+ this.#response = "";
// we need this promise here because await nsIHttpChannel::asyncOpen does
// not return only once the request is complete, it seems to return
// after it begins, so we have to get the result from this listener object.
// This promise is only resolved once onStopRequest is called
- this._responsePromise = new Promise((resolve, reject) => {
- this._resolve = resolve;
- this._reject = reject;
+ this.#responsePromise = new Promise((resolve, reject) => {
+ this.#resolve = resolve;
+ this.#reject = reject;
});
}
// callers wait on this for final response
response() {
- return this._responsePromise;
+ return this.#responsePromise;
}
// noop
@@ -352,16 +319,17 @@ class MoatResponseListener {
onStopRequest(request, status) {
try {
if (!Components.isSuccessCode(status)) {
- const errorMessage = TorLauncherUtil.getLocalizedStringForError(status);
- this._reject(new Error(errorMessage));
+ const errorMessage =
+ lazy.TorLauncherUtil.getLocalizedStringForError(status);
+ this.#reject(new Error(errorMessage));
}
if (request.responseStatus != 200) {
- this._reject(new Error(request.responseStatusText));
+ this.#reject(new Error(request.responseStatusText));
}
} catch (err) {
- this._reject(err);
+ this.#reject(err);
}
- this._resolve(this._response);
+ this.#resolve(this.#response);
}
// read response data
@@ -370,30 +338,32 @@ class MoatResponseListener {
"@mozilla.org/scriptableinputstream;1"
].createInstance(Ci.nsIScriptableInputStream);
scriptableStream.init(stream);
- this._response += scriptableStream.read(length);
+ this.#response += scriptableStream.read(length);
}
}
class InternetTestResponseListener {
+ #promise;
+ #resolve;
+ #reject;
constructor() {
- this._promise = new Promise((resolve, reject) => {
- this._resolve = resolve;
- this._reject = reject;
+ this.#promise = new Promise((resolve, reject) => {
+ this.#resolve = resolve;
+ this.#reject = reject;
});
}
// callers wait on this for final response
get status() {
- return this._promise;
+ return this.#promise;
}
onStartRequest(request) {}
// resolve or reject our Promise
onStopRequest(request, status) {
- let statuses = {};
try {
- statuses = {
+ const statuses = {
components: status,
successful: Components.isSuccessCode(status),
};
@@ -408,56 +378,51 @@ class InternetTestResponseListener {
err
);
}
+ this.#resolve(statuses);
} catch (err) {
- this._reject(err);
+ this.#reject(err);
}
- this._resolve(statuses);
}
onDataAvailable(request, stream, offset, length) {
- // We do not care of the actual data, as long as we have a successful
+ // We do not care of the actual data, as long as we have a successful
// connection
}
}
// constructs the json objects and sends the request over moat
-class MoatRPC {
- constructor() {
- this._meekTransport = null;
- this._inited = false;
- }
+export class MoatRPC {
+ #inited = false;
+ #meekTransport = null;
get inited() {
- return this._inited;
+ return this.#inited;
}
async init() {
- if (this._inited) {
+ if (this.#inited) {
throw new Error("MoatRPC: Already initialized");
}
let meekTransport = new MeekTransport();
await meekTransport.init();
- this._meekTransport = meekTransport;
- this._inited = true;
+ this.#meekTransport = meekTransport;
+ this.#inited = true;
}
async uninit() {
- await this._meekTransport?.uninit();
- this._meekTransport = null;
- this._inited = false;
+ await this.#meekTransport?.uninit();
+ this.#meekTransport = null;
+ this.#inited = false;
}
- _makeHttpHandler(uriString) {
- if (!this._inited) {
+ #makeHttpHandler(uriString) {
+ if (!this.#inited) {
throw new Error("MoatRPC: Not initialized");
}
- const proxyType = this._meekTransport._meekProxyType;
- const proxyAddress = this._meekTransport._meekProxyAddress;
- const proxyPort = this._meekTransport._meekProxyPort;
- const proxyUsername = this._meekTransport._meekProxyUsername;
- const proxyPassword = this._meekTransport._meekProxyPassword;
+ const { proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword } =
+ this.#meekTransport;
const proxyPS = Cc[
"@mozilla.org/network/protocol-proxy-service;1"
@@ -511,11 +476,11 @@ class MoatRPC {
return ch;
}
- async _makeRequest(procedure, args) {
+ async #makeRequest(procedure, args) {
const procedureURIString = `${Services.prefs.getStringPref(
TorLauncherPrefs.moat_service
)}/${procedure}`;
- const ch = this._makeHttpHandler(procedureURIString);
+ const ch = this.#makeHttpHandler(procedureURIString);
// Arrange for the POST data to be sent.
const argsJson = JSON.stringify(args);
@@ -544,7 +509,7 @@ class MoatRPC {
const uri = `${Services.prefs.getStringPref(
TorLauncherPrefs.moat_service
)}/circumvention/countries`;
- const ch = this._makeHttpHandler(uri);
+ const ch = this.#makeHttpHandler(uri);
ch.requestMethod = "HEAD";
const listener = new InternetTestResponseListener();
@@ -582,7 +547,7 @@ class MoatRPC {
},
],
};
- const response = await this._makeRequest("fetch", args);
+ const response = await this.#makeRequest("fetch", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
@@ -623,7 +588,7 @@ class MoatRPC {
},
],
};
- const response = await this._makeRequest("check", args);
+ const response = await this.#makeRequest("check", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
@@ -642,7 +607,7 @@ class MoatRPC {
// Convert received settings object to format used by TorSettings module
// In the event of error, just return null
- _fixupSettings(settings) {
+ #fixupSettings(settings) {
try {
let retval = TorSettings.defaultSettings();
if ("bridges" in settings) {
@@ -691,11 +656,11 @@ class MoatRPC {
// Converts a list of settings objects received from BridgeDB to a list of settings objects
// understood by the TorSettings module
// In the event of error, returns and empty list
- _fixupSettingsList(settingsList) {
+ #fixupSettingsList(settingsList) {
try {
let retval = [];
for (let settings of settingsList) {
- settings = this._fixupSettings(settings);
+ settings = this.#fixupSettings(settings);
if (settings != null) {
retval.push(settings);
}
@@ -724,7 +689,7 @@ class MoatRPC {
transports: transports ? transports : [],
country,
};
- const response = await this._makeRequest("circumvention/settings", args);
+ const response = await this.#makeRequest("circumvention/settings", args);
let settings = {};
if ("errors" in response) {
const code = response.errors[0].code;
@@ -739,7 +704,7 @@ class MoatRPC {
throw new Error(`MoatRPC: ${detail} (${code})`);
} else if ("settings" in response) {
- settings.settings = this._fixupSettingsList(response.settings);
+ settings.settings = this.#fixupSettingsList(response.settings);
}
if ("country" in response) {
settings.country = response.country;
@@ -753,7 +718,7 @@ class MoatRPC {
// for
async circumvention_countries() {
const args = {};
- return this._makeRequest("circumvention/countries", args);
+ return this.#makeRequest("circumvention/countries", args);
}
// Request a copy of the builtin bridges, takes the following parameters:
@@ -766,7 +731,7 @@ class MoatRPC {
const args = {
transports: transports ? transports : [],
};
- const response = await this._makeRequest("circumvention/builtin", args);
+ const response = await this.#makeRequest("circumvention/builtin", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
@@ -791,13 +756,13 @@ class MoatRPC {
const args = {
transports: transports ? transports : [],
};
- const response = await this._makeRequest("circumvention/defaults", args);
+ const response = await this.#makeRequest("circumvention/defaults", args);
if ("errors" in response) {
const code = response.errors[0].code;
const detail = response.errors[0].detail;
throw new Error(`MoatRPC: ${detail} (${code})`);
} else if ("settings" in response) {
- return this._fixupSettingsList(response.settings);
+ return this.#fixupSettingsList(response.settings);
}
return [];
}
=====================================
browser/modules/TorConnect.jsm → browser/modules/TorConnect.sys.mjs
=====================================
@@ -1,36 +1,32 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = [
- "InternetStatus",
- "TorConnect",
- "TorConnectTopics",
- "TorConnectState",
-];
+import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const lazy = {};
-const { setTimeout, clearTimeout } = ChromeUtils.import(
- "resource://gre/modules/Timer.jsm"
-);
+ChromeUtils.defineESModuleGetters(lazy, {
+ MoatRPC: "resource:///modules/Moat.sys.mjs",
+ TorBootstrapRequest: "resource://gre/modules/TorBootstrapRequest.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
-const { BrowserWindowTracker } = ChromeUtils.import(
+// TODO: Should we move this to the about:torconnect actor?
+ChromeUtils.defineModuleGetter(
+ lazy,
+ "BrowserWindowTracker",
"resource:///modules/BrowserWindowTracker.jsm"
);
-const { TorMonitorService } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
-);
-const { TorBootstrapRequest } = ChromeUtils.import(
- "resource://gre/modules/TorBootstrapRequest.jsm"
-);
-
-const { TorSettings, TorSettingsTopics, TorBuiltinBridgeTypes } =
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
+import {
+ TorSettings,
+ TorSettingsTopics,
+ TorBuiltinBridgeTypes,
+} from "resource:///modules/TorSettings.sys.mjs";
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
-
const TorTopics = Object.freeze({
LogHasWarnOrErr: "TorLogHasWarnOrErr",
ProcessExited: "TorProcessExited",
@@ -46,7 +42,7 @@ const TorConnectPrefs = Object.freeze({
allow_internet_test: "torbrowser.bootstrap.allow_internet_test",
});
-const TorConnectState = Object.freeze({
+export const TorConnectState = Object.freeze({
/* Our initial state */
Initial: "Initial",
/* In-between initial boot and bootstrapping, users can change tor network settings during this state */
@@ -156,7 +152,7 @@ const TorConnectStateTransitions = Object.freeze(
);
/* Topics Notified by the TorConnect module */
-const TorConnectTopics = Object.freeze({
+export const TorConnectTopics = Object.freeze({
StateChange: "torconnect:state-change",
BootstrapProgress: "torconnect:bootstrap-progress",
BootstrapComplete: "torconnect:bootstrap-complete",
@@ -238,7 +234,7 @@ const debug_sleep = async ms => {
});
};
-const InternetStatus = Object.freeze({
+export const InternetStatus = Object.freeze({
Unknown: -1,
Offline: 0,
Online: 1,
@@ -302,7 +298,7 @@ class InternetTest {
// waiting both for the bootstrap, and for the Internet test.
// However, managing Moat with async/await is much easier as it avoids a
// callback hell, and it makes extra explicit that we are uniniting it.
- const mrpc = new MoatRPC();
+ const mrpc = new lazy.MoatRPC();
let status = null;
let error = null;
try {
@@ -340,7 +336,7 @@ class InternetTest {
}
}
-const TorConnect = (() => {
+export const TorConnect = (() => {
let retval = {
_state: TorConnectState.Initial,
_bootstrapProgress: 0,
@@ -459,7 +455,7 @@ const TorConnect = (() => {
return;
}
- const tbr = new TorBootstrapRequest();
+ const tbr = new lazy.TorBootstrapRequest();
const internetTest = new InternetTest();
let cancelled = false;
@@ -604,7 +600,7 @@ const TorConnect = (() => {
// lookup user's potential censorship circumvention settings from Moat service
try {
- this.mrpc = new MoatRPC();
+ this.mrpc = new lazy.MoatRPC();
await this.mrpc.init();
if (this.transitioning) {
@@ -678,7 +674,7 @@ const TorConnect = (() => {
await TorSettings.applySettings();
// build out our bootstrap request
- const tbr = new TorBootstrapRequest();
+ const tbr = new lazy.TorBootstrapRequest();
tbr.onbootstrapstatus = (progress, status) => {
TorConnect._updateBootstrapStatus(progress, status);
};
@@ -915,7 +911,7 @@ const TorConnect = (() => {
* @type {boolean}
*/
get enabled() {
- return TorMonitorService.ownsTorDaemon;
+ return lazy.TorProviderBuilder.build().ownsTorDaemon;
},
get shouldShowTorConnect() {
@@ -1053,7 +1049,7 @@ const TorConnect = (() => {
Further external commands and helper methods
*/
openTorPreferences() {
- const win = BrowserWindowTracker.getTopWindow();
+ const win = lazy.BrowserWindowTracker.getTopWindow();
win.switchToTabHavingURI("about:preferences#connection", true);
},
@@ -1073,7 +1069,7 @@ const TorConnect = (() => {
* begin AutoBootstrapping, if possible.
*/
openTorConnect(options) {
- const win = BrowserWindowTracker.getTopWindow();
+ const win = lazy.BrowserWindowTracker.getTopWindow();
win.switchToTabHavingURI("about:torconnect", true, {
ignoreQueryString: true,
});
@@ -1094,7 +1090,7 @@ const TorConnect = (() => {
},
viewTorLogs() {
- const win = BrowserWindowTracker.getTopWindow();
+ const win = lazy.BrowserWindowTracker.getTopWindow();
win.switchToTabHavingURI("about:preferences#connection-viewlogs", true);
},
@@ -1104,7 +1100,7 @@ const TorConnect = (() => {
if (this._countryCodes.length) {
return this._countryCodes;
}
- const mrpc = new MoatRPC();
+ const mrpc = new lazy.MoatRPC();
try {
await mrpc.init();
this._countryCodes = await mrpc.circumvention_countries();
=====================================
browser/modules/TorSettings.jsm → browser/modules/TorSettings.sys.mjs
=====================================
@@ -1,36 +1,22 @@
-"use strict";
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-var EXPORTED_SYMBOLS = [
- "TorSettings",
- "TorSettingsTopics",
- "TorSettingsData",
- "TorBridgeSource",
- "TorBuiltinBridgeTypes",
- "TorProxyType",
-];
+const lazy = {};
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-const { TorMonitorService } = ChromeUtils.import(
- "resource://gre/modules/TorMonitorService.jsm"
-);
-const { TorProtocolService } = ChromeUtils.import(
- "resource://gre/modules/TorProtocolService.jsm"
-);
-
-/* tor-launcher observer topics */
-const TorTopics = Object.freeze({
- ProcessIsReady: "TorProcessIsReady",
+ChromeUtils.defineESModuleGetters(lazy, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+ TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
});
/* TorSettings observer topics */
-const TorSettingsTopics = Object.freeze({
+export const TorSettingsTopics = Object.freeze({
Ready: "torsettings:ready",
SettingChanged: "torsettings:setting-changed",
});
/* TorSettings observer data (for SettingChanged topic) */
-const TorSettingsData = Object.freeze({
+export const TorSettingsData = Object.freeze({
QuickStartEnabled: "torsettings:quickstart_enabled",
});
@@ -98,21 +84,21 @@ const TorConfigKeys = Object.freeze({
clientTransportPlugin: "ClientTransportPlugin",
});
-const TorBridgeSource = Object.freeze({
+export const TorBridgeSource = Object.freeze({
Invalid: -1,
BuiltIn: 0,
BridgeDB: 1,
UserProvided: 2,
});
-const TorProxyType = Object.freeze({
+export const TorProxyType = Object.freeze({
Invalid: -1,
Socks4: 0,
Socks5: 1,
HTTPS: 2,
});
-const TorBuiltinBridgeTypes = Object.freeze(
+export const TorBuiltinBridgeTypes = Object.freeze(
(() => {
const bridgeListBranch = Services.prefs.getBranch(
TorLauncherPrefs.default_bridge
@@ -254,7 +240,7 @@ const arrayCopy = function (array) {
/* TorSettings module */
-const TorSettings = (() => {
+export const TorSettings = (() => {
const self = {
_settings: null,
@@ -288,7 +274,8 @@ const TorSettings = (() => {
/* load or init our settings, and register observers */
init() {
- if (TorMonitorService.ownsTorDaemon) {
+ const provider = lazy.TorProviderBuilder.build();
+ if (provider.ownsTorDaemon) {
// if the settings branch exists, load settings from prefs
if (Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)) {
this.loadFromPrefs();
@@ -296,9 +283,9 @@ const TorSettings = (() => {
// otherwise load defaults
this._settings = this.defaultSettings();
}
- Services.obs.addObserver(this, TorTopics.ProcessIsReady);
+ Services.obs.addObserver(this, lazy.TorProviderTopics.ProcessIsReady);
- if (TorMonitorService.isRunning) {
+ if (provider.isRunning) {
this.handleProcessReady();
}
}
@@ -309,8 +296,11 @@ const TorSettings = (() => {
console.log(`TorSettings: Observed ${topic}`);
switch (topic) {
- case TorTopics.ProcessIsReady:
- Services.obs.removeObserver(this, TorTopics.ProcessIsReady);
+ case lazy.TorProviderTopics.ProcessIsReady:
+ Services.obs.removeObserver(
+ this,
+ lazy.TorProviderTopics.ProcessIsReady
+ );
await this.handleProcessReady();
break;
}
@@ -569,7 +559,7 @@ const TorSettings = (() => {
}
/* Push to Tor */
- await TorProtocolService.writeSettings(settingsMap);
+ await lazy.TorProviderBuilder.build().writeSettings(settingsMap);
return this;
},
=====================================
browser/modules/moz.build
=====================================
@@ -123,7 +123,7 @@ XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"]
EXTRA_JS_MODULES += [
"AboutNewTab.jsm",
"AsyncTabSwitcher.jsm",
- "BridgeDB.jsm",
+ "BridgeDB.sys.mjs",
"BrowserUIUtils.jsm",
"BrowserUsageTelemetry.jsm",
"BrowserWindowTracker.jsm",
@@ -135,7 +135,7 @@ EXTRA_JS_MODULES += [
"FeatureCallout.sys.mjs",
"HomePage.jsm",
"LaterRun.jsm",
- 'Moat.jsm',
+ "Moat.sys.mjs",
"NewTabPagePreloading.jsm",
"OpenInTabsUtils.jsm",
"PageActions.jsm",
@@ -149,8 +149,8 @@ EXTRA_JS_MODULES += [
"SitePermissions.sys.mjs",
"TabsList.jsm",
"TabUnloader.jsm",
- "TorConnect.jsm",
- "TorSettings.jsm",
+ "TorConnect.sys.mjs",
+ "TorSettings.sys.mjs",
"TorStrings.jsm",
"TransientPrefs.jsm",
"URILoadingHelper.sys.mjs",
=====================================
toolkit/components/tor-launcher/TorBootstrapRequest.sys.mjs
=====================================
@@ -1,6 +1,6 @@
import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
-import { TorProtocolService } from "resource://gre/modules/TorProtocolService.sys.mjs";
+import { TorProviderBuilder } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs";
/* tor-launcher observer topics */
@@ -13,19 +13,23 @@ export const TorTopics = Object.freeze({
// modeled after XMLHttpRequest
// nicely encapsulates the observer register/unregister logic
export class TorBootstrapRequest {
+ // number of ms to wait before we abandon the bootstrap attempt
+ // a value of 0 implies we never wait
+ timeout = 0;
+
+ // callbacks for bootstrap process status updates
+ onbootstrapstatus = (progress, status) => {};
+ onbootstrapcomplete = () => {};
+ onbootstraperror = (message, details) => {};
+
+ // internal resolve() method for bootstrap
+ #bootstrapPromiseResolve = null;
+ #bootstrapPromise = null;
+ #timeoutID = null;
+ #provider = null;
+
constructor() {
- // number of ms to wait before we abandon the bootstrap attempt
- // a value of 0 implies we never wait
- this.timeout = 0;
- // callbacks for bootstrap process status updates
- this.onbootstrapstatus = (progress, status) => {};
- this.onbootstrapcomplete = () => {};
- this.onbootstraperror = (message, details) => {};
-
- // internal resolve() method for bootstrap
- this._bootstrapPromiseResolve = null;
- this._bootstrapPromise = null;
- this._timeoutID = null;
+ this.#provider = TorProviderBuilder.build();
}
observe(subject, topic, data) {
@@ -41,15 +45,16 @@ export class TorBootstrapRequest {
if (this.onbootstrapcomplete) {
this.onbootstrapcomplete();
}
- this._bootstrapPromiseResolve(true);
- clearTimeout(this._timeoutID);
+ this.#bootstrapPromiseResolve(true);
+ clearTimeout(this.#timeoutID);
+ this.#timeoutID = null;
}
break;
}
case TorTopics.BootstrapError: {
console.info("TorBootstrapRequest: observerd TorBootstrapError", obj);
- this._stop(obj?.message, obj?.details);
+ this.#stop(obj?.message, obj?.details);
break;
}
}
@@ -57,12 +62,12 @@ export class TorBootstrapRequest {
// resolves 'true' if bootstrap succeeds, false otherwise
bootstrap() {
- if (this._bootstrapPromise) {
- return this._bootstrapPromise;
+ if (this.#bootstrapPromise) {
+ return this.#bootstrapPromise;
}
- this._bootstrapPromise = new Promise((resolve, reject) => {
- this._bootstrapPromiseResolve = resolve;
+ this.#bootstrapPromise = new Promise((resolve, reject) => {
+ this.#bootstrapPromiseResolve = resolve;
// register ourselves to listen for bootstrap events
Services.obs.addObserver(this, TorTopics.BootstrapStatus);
@@ -70,10 +75,10 @@ export class TorBootstrapRequest {
// optionally cancel bootstrap after a given timeout
if (this.timeout > 0) {
- this._timeoutID = setTimeout(async () => {
- this._timeoutID = null;
+ this.#timeoutID = setTimeout(async () => {
+ this.#timeoutID = null;
// TODO: Translate, if really used
- await this._stop(
+ await this.#stop(
"Tor Bootstrap process timed out",
`Bootstrap attempt abandoned after waiting ${this.timeout} ms`
);
@@ -81,38 +86,45 @@ export class TorBootstrapRequest {
}
// wait for bootstrapping to begin and maybe handle error
- TorProtocolService.connect().catch(err => {
- this._stop(err.message, "");
+ this.#provider.connect().catch(err => {
+ this.#stop(err.message, "");
});
}).finally(() => {
// and remove ourselves once bootstrap is resolved
Services.obs.removeObserver(this, TorTopics.BootstrapStatus);
Services.obs.removeObserver(this, TorTopics.BootstrapError);
- this._bootstrapPromise = null;
+ this.#bootstrapPromise = null;
});
- return this._bootstrapPromise;
+ return this.#bootstrapPromise;
}
async cancel() {
- await this._stop();
+ await this.#stop();
}
// Internal implementation. Do not use directly, but call cancel, instead.
- async _stop(message, details) {
+ async #stop(message, details) {
// first stop our bootstrap timeout before handling the error
- if (this._timeoutID !== null) {
- clearTimeout(this._timeoutID);
- this._timeoutID = null;
+ if (this.#timeoutID !== null) {
+ clearTimeout(this.#timeoutID);
+ this.#timeoutID = null;
}
- // stopBootstrap never throws
- await TorProtocolService.stopBootstrap();
+ try {
+ await this.#provider.stopBootstrap();
+ } catch (e) {
+ console.error("Failed to stop the bootstrap.", e);
+ if (!message) {
+ message = e.message;
+ details = "";
+ }
+ }
if (this.onbootstraperror && message) {
this.onbootstraperror(message, details);
}
- this._bootstrapPromiseResolve(false);
+ this.#bootstrapPromiseResolve(false);
}
}
=====================================
toolkit/components/tor-launcher/TorControlPort.sys.mjs
=====================================
@@ -274,6 +274,44 @@ class AsyncSocket {
* the command
*/
+/**
+ * @typedef {object} Bridge
+ * @property {string} transport The transport of the bridge, or vanilla if not
+ * specified.
+ * @property {string} addr The IP address and port of the bridge
+ * @property {string} id The fingerprint of the bridge
+ * @property {string} args Optional arguments passed to the bridge
+ */
+/**
+ * @typedef {object} PTInfo The information about a pluggable transport
+ * @property {string[]} transports An array with all the transports supported by
+ * this configuration.
+ * @property {string} type Either socks4, socks5 or exec
+ * @property {string} [ip] The IP address of the proxy (only for socks4 and
+ * socks5)
+ * @property {integer} [port] The port of the proxy (only for socks4 and socks5)
+ * @property {string} [pathToBinary] Path to the binary that is run (only for
+ * exec)
+ * @property {string} [options] Optional options passed to the binary (only for
+ * exec)
+ */
+/**
+ * @typedef {object} OnionAuthKeyInfo
+ * @property {string} address The address of the onion service
+ * @property {string} typeAndKey Onion service key and type of key, as
+ * `type:base64-private-key`
+ * @property {string} Flags Additional flags, such as Permanent
+ */
+/**
+ * @callback EventFilterCallback
+ * @param {any} data Either a raw string, or already parsed data
+ * @returns {boolean}
+ */
+/**
+ * @callback EventCallback
+ * @param {any} data Either a raw string, or already parsed data
+ */
+
class TorError extends Error {
constructor(command, reply) {
super(`${command} -> ${reply}`);
@@ -584,319 +622,6 @@ class ControlSocket {
}
}
-// ## utils
-// A namespace for utility functions
-let utils = {};
-
-// __utils.identity(x)__.
-// Returns its argument unchanged.
-utils.identity = function (x) {
- return x;
-};
-
-// __utils.capture(string, regex)__.
-// Takes a string and returns an array of capture items, where regex must have a single
-// capturing group and use the suffix /.../g to specify a global search.
-utils.capture = function (string, regex) {
- let matches = [];
- // Special trick to use string.replace for capturing multiple matches.
- string.replace(regex, function (a, captured) {
- matches.push(captured);
- });
- return matches;
-};
-
-// __utils.extractor(regex)__.
-// Returns a function that takes a string and returns an array of regex matches. The
-// regex must use the suffix /.../g to specify a global search.
-utils.extractor = function (regex) {
- return function (text) {
- return utils.capture(text, regex);
- };
-};
-
-// __utils.splitLines(string)__.
-// Splits a string into an array of strings, each corresponding to a line.
-utils.splitLines = function (string) {
- return string.split(/\r?\n/);
-};
-
-// __utils.splitAtSpaces(string)__.
-// Splits a string into chunks between spaces. Does not split at spaces
-// inside pairs of quotation marks.
-utils.splitAtSpaces = utils.extractor(/((\S*?"(.*?)")+\S*|\S+)/g);
-
-// __utils.splitAtFirst(string, regex)__.
-// Splits a string at the first instance of regex match. If no match is
-// found, returns the whole string.
-utils.splitAtFirst = function (string, regex) {
- let match = string.match(regex);
- return match
- ? [
- string.substring(0, match.index),
- string.substring(match.index + match[0].length),
- ]
- : string;
-};
-
-// __utils.splitAtEquals(string)__.
-// Splits a string into chunks between equals. Does not split at equals
-// inside pairs of quotation marks.
-utils.splitAtEquals = utils.extractor(/(([^=]*?"(.*?)")+[^=]*|[^=]+)/g);
-
-// __utils.mergeObjects(arrayOfObjects)__.
-// Takes an array of objects like [{"a":"b"},{"c":"d"}] and merges to a single object.
-// Pure function.
-utils.mergeObjects = function (arrayOfObjects) {
- let result = {};
- for (let obj of arrayOfObjects) {
- for (let key in obj) {
- result[key] = obj[key];
- }
- }
- return result;
-};
-
-// __utils.listMapData(parameterString, listNames)__.
-// Takes a list of parameters separated by spaces, of which the first several are
-// unnamed, and the remainder are named, in the form `NAME=VALUE`. Apply listNames
-// to the unnamed parameters, and combine them in a map with the named parameters.
-// Example: `40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH`
-//
-// utils.listMapData("40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH",
-// ["streamID", "event", "circuitID", "IP"])
-// // --> {"streamID" : "40", "event" : "FAILED", "circuitID" : "0",
-// // "address" : "95.78.59.36:80", "REASON" : "CANT_ATTACH"}"
-utils.listMapData = function (parameterString, listNames) {
- // Split out the space-delimited parameters.
- let parameters = utils.splitAtSpaces(parameterString),
- dataMap = {};
- // Assign listNames to the first n = listNames.length parameters.
- for (let i = 0; i < listNames.length; ++i) {
- dataMap[listNames[i]] = parameters[i];
- }
- // Read key-value pairs and copy these to the dataMap.
- for (let i = listNames.length; i < parameters.length; ++i) {
- let [key, value] = utils.splitAtEquals(parameters[i]);
- if (key && value) {
- dataMap[key] = value;
- }
- }
- return dataMap;
-};
-
-// ## info
-// A namespace for functions related to tor's GETINFO and GETCONF command.
-let info = {};
-
-// __info.keyValueStringsFromMessage(messageText)__.
-// Takes a message (text) response to GETINFO or GETCONF and provides
-// a series of key-value strings, which are either multiline (with a `250+` prefix):
-//
-// 250+config/defaults=
-// AccountingMax "0 bytes"
-// AllowDotExit "0"
-// .
-//
-// or single-line (with a `250-` or `250 ` prefix):
-//
-// 250-version=0.2.6.0-alpha-dev (git-b408125288ad6943)
-info.keyValueStringsFromMessage = utils.extractor(
- /^(250\+[\s\S]+?^\.|250[- ].+?)$/gim
-);
-
-// __info.applyPerLine(transformFunction)__.
-// Returns a function that splits text into lines,
-// and applies transformFunction to each line.
-info.applyPerLine = function (transformFunction) {
- return function (text) {
- return utils.splitLines(text.trim()).map(transformFunction);
- };
-};
-
-// __info.routerStatusParser(valueString)__.
-// Parses a router status entry as, described in
-// https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt
-// (search for "router status entry")
-info.routerStatusParser = function (valueString) {
- let lines = utils.splitLines(valueString),
- objects = [];
- for (let line of lines) {
- // Drop first character and grab data following it.
- let myData = line.substring(2),
- // Accumulate more maps with data, depending on the first character in the line.
- dataFun = {
- r: data =>
- utils.listMapData(data, [
- "nickname",
- "identity",
- "digest",
- "publicationDate",
- "publicationTime",
- "IP",
- "ORPort",
- "DirPort",
- ]),
- a: data => ({ IPv6: data }),
- s: data => ({ statusFlags: utils.splitAtSpaces(data) }),
- v: data => ({ version: data }),
- w: data => utils.listMapData(data, []),
- p: data => ({ portList: data.split(",") }),
- }[line.charAt(0)];
- if (dataFun !== undefined) {
- objects.push(dataFun(myData));
- }
- }
- return utils.mergeObjects(objects);
-};
-
-// __info.circuitStatusParser(line)__.
-// Parse the output of a circuit status line.
-info.circuitStatusParser = function (line) {
- let data = utils.listMapData(line, ["id", "status", "circuit"]),
- circuit = data.circuit;
- // Parse out the individual circuit IDs and names.
- if (circuit) {
- data.circuit = circuit.split(",").map(function (x) {
- return x.split(/~|=/);
- });
- }
- return data;
-};
-
-// __info.streamStatusParser(line)__.
-// Parse the output of a stream status line.
-info.streamStatusParser = function (text) {
- return utils.listMapData(text, [
- "StreamID",
- "StreamStatus",
- "CircuitID",
- "Target",
- ]);
-};
-
-// TODO: fix this parsing logic to handle bridgeLine correctly
-// fingerprint/id is an optional parameter
-// __info.bridgeParser(bridgeLine)__.
-// Takes a single line from a `getconf bridge` result and returns
-// a map containing the bridge's type, address, and ID.
-info.bridgeParser = function (bridgeLine) {
- let result = {},
- tokens = bridgeLine.split(/\s+/);
- // First check if we have a "vanilla" bridge:
- if (tokens[0].match(/^\d+\.\d+\.\d+\.\d+/)) {
- result.type = "vanilla";
- [result.address, result.ID] = tokens;
- // Several bridge types have a similar format:
- } else {
- result.type = tokens[0];
- if (
- [
- "flashproxy",
- "fte",
- "meek",
- "meek_lite",
- "obfs3",
- "obfs4",
- "scramblesuit",
- "snowflake",
- ].includes(result.type)
- ) {
- [result.address, result.ID] = tokens.slice(1);
- }
- }
- return result.type ? result : null;
-};
-
-// __info.parsers__.
-// A map of GETINFO and GETCONF keys to parsing function, which convert
-// result strings to JavaScript data.
-info.parsers = {
- "ns/id/": info.routerStatusParser,
- "ip-to-country/": utils.identity,
- "circuit-status": info.applyPerLine(info.circuitStatusParser),
- bridge: info.bridgeParser,
- // Currently unused parsers:
- // "ns/name/" : info.routerStatusParser,
- // "stream-status" : info.applyPerLine(info.streamStatusParser),
- // "version" : utils.identity,
- // "config-file" : utils.identity,
-};
-
-// __info.getParser(key)__.
-// Takes a key and determines the parser function that should be used to
-// convert its corresponding valueString to JavaScript data.
-info.getParser = function (key) {
- return (
- info.parsers[key] ||
- info.parsers[key.substring(0, key.lastIndexOf("/") + 1)]
- );
-};
-
-// __info.stringToValue(string)__.
-// Converts a key-value string as from GETINFO or GETCONF to a value.
-info.stringToValue = function (string) {
- // key should look something like `250+circuit-status=` or `250-circuit-status=...`
- // or `250 circuit-status=...`
- let matchForKey = string.match(/^250[ +-](.+?)=/),
- key = matchForKey ? matchForKey[1] : null;
- if (key === null) {
- return null;
- }
- // matchResult finds a single-line result for `250-` or `250 `,
- // or a multi-line one for `250+`.
- let matchResult =
- string.match(/^250[ -].+?=(.*)$/) ||
- string.match(/^250\+.+?=([\s\S]*?)^\.$/m),
- // Retrieve the captured group (the text of the value in the key-value pair)
- valueString = matchResult ? matchResult[1] : null,
- // Get the parser function for the key found.
- parse = info.getParser(key.toLowerCase());
- if (parse === undefined) {
- throw new Error("No parser found for '" + key + "'");
- }
- // Return value produced by the parser.
- return parse(valueString);
-};
-
-/**
- * @typedef {object} Bridge
- * @property {string} transport The transport of the bridge, or vanilla if not
- * specified.
- * @property {string} addr The IP address and port of the bridge
- * @property {string} id The fingerprint of the bridge
- * @property {string} args Optional arguments passed to the bridge
- */
-/**
- * @typedef {object} PTInfo The information about a pluggable transport
- * @property {string[]} transports An array with all the transports supported by
- * this configuration.
- * @property {string} type Either socks4, socks5 or exec
- * @property {string} [ip] The IP address of the proxy (only for socks4 and
- * socks5)
- * @property {integer} [port] The port of the proxy (only for socks4 and socks5)
- * @property {string} [pathToBinary] Path to the binary that is run (only for
- * exec)
- * @property {string} [options] Optional options passed to the binary (only for
- * exec)
- */
-/**
- * @typedef {object} OnionAuthKeyInfo
- * @property {string} address The address of the onion service
- * @property {string} typeAndKey Onion service key and type of key, as
- * `type:base64-private-key`
- * @property {string} Flags Additional flags, such as Permanent
- */
-/**
- * @callback EventFilterCallback
- * @param {any} data Either a raw string, or already parsed data
- * @returns {boolean}
- */
-/**
- * @callback EventCallback
- * @param {any} data Either a raw string, or already parsed data
- */
-
class TorController {
/**
* The control socket
@@ -905,16 +630,6 @@ class TorController {
*/
#socket;
- /**
- * A map of EVENT keys to parsing functions, which convert result strings to
- * JavaScript data.
- */
- #eventParsers = {
- stream: info.streamStatusParser,
- // Currently unused:
- // "circ" : info.circuitStatusParser,
- };
-
/**
* Builds a new TorController.
*
@@ -981,18 +696,6 @@ class TorController {
await this.#sendCommandSimple(`authenticate ${password || ""}`);
}
- /**
- * Sends a GETINFO for a single key.
- *
- * @param {string} key The key to get value for
- * @returns {any} The return value depends on the requested key
- */
- async getInfo(key) {
- this.#expectString(key, "key");
- const response = await this.sendCommand(`getinfo ${key}`);
- return this.#getMultipleResponseValues(response)[0];
- }
-
/**
* Sends a GETINFO for a single key.
* control-spec.txt says "one ReplyLine is sent for each requested value", so,
@@ -1054,9 +757,7 @@ class TorController {
const addresses = [v4[5]];
// a address:port
// dir-spec.txt also states only the first one should be taken
- // TODO: The consumers do not care about the port or the square brackets
- // either. Remove them when integrating this function with the rest
- const v6 = reply.match(/^a\s+(\[[0-9a-fA-F:]+\]:[0-9]{1,5})$/m);
+ const v6 = reply.match(/^a\s+\[([0-9a-fA-F:]+)\]:\d{1,5}$/m);
if (v6) {
addresses.push(v6[1]);
}
@@ -1091,23 +792,6 @@ class TorController {
// Configuration
- /**
- * Sends a GETCONF for a single key.
- * GETCONF with a single argument returns results with one or more lines that
- * look like `250[- ]key=value`.
- * Any GETCONF lines that contain a single keyword only are currently dropped.
- * So we can use similar parsing to that for getInfo.
- *
- * @param {string} key The key to get value for
- * @returns {any} A parsed config value (it depends if a parser is known)
- */
- async getConf(key) {
- this.#expectString(key, "key");
- return this.#getMultipleResponseValues(
- await this.sendCommand(`getconf ${key}`)
- );
- }
-
/**
* Sends a GETCONF for a single key.
* The function could be easily generalized to get multiple keys at once, but
@@ -1264,12 +948,14 @@ class TorController {
// TODO: Change the consumer and make the fields more consistent with what
// we get (e.g., separate key and type, and use a boolen for permanent).
const info = {
- hsAddress: match.groups.HSAddress,
- typeAndKey: `${match.groups.KeyType}:${match.groups.PrivateKeyBlob}`,
+ address: match.groups.HSAddress,
+ keyType: match.groups.KeyType,
+ keyBlob: match.groups.PrivateKeyBlob,
+ flags: [],
};
const maybeFlags = match.groups.other?.match(/Flags=(\S+)/);
if (maybeFlags) {
- info.Flags = maybeFlags[1];
+ info.flags = maybeFlags[1].split(",");
}
return info;
});
@@ -1369,28 +1055,12 @@ class TorController {
* first.
*
* @param {string} type The event type to catch
- * @param {EventFilterCallback?} filter An optional callback to filter
- * events for which the callback will be called. If null, all events will be
- * passed.
* @param {EventCallback} callback The callback that will handle the event
- * @param {boolean} raw Tell whether to ignore the data parser, even if
- * supported
*/
- watchEvent(type, filter, callback, raw = false) {
+ watchEvent(type, callback) {
this.#expectString(type, "type");
const start = `650 ${type}`;
- this.#socket.addNotificationCallback(new RegExp(`^${start}`), message => {
- // Remove also the initial text
- const dataText = message.substring(start.length + 1);
- const parser = this.#eventParsers[type.toLowerCase()];
- const data = dataText && parser ? parser(dataText) : null;
- // FIXME: This is the original code, but we risk of not filtering on the
- // data, if we ask for raw data (which we always do at the moment, but we
- // do not use a filter either...)
- if (filter === null || filter(data)) {
- callback(data && !raw ? data : message);
- }
- });
+ this.#socket.addNotificationCallback(new RegExp(`^${start}`), callback);
}
// Other helpers
@@ -1453,19 +1123,6 @@ class TorController {
)
);
}
-
- /**
- * Process multiple responses to a GETINFO or GETCONF request.
- *
- * @param {string} message The message to process
- * @returns {object[]} The keys depend on the message
- */
- #getMultipleResponseValues(message) {
- return info
- .keyValueStringsFromMessage(message)
- .map(info.stringToValue)
- .filter(x => x);
- }
}
const controlPortInfo = {};
=====================================
toolkit/components/tor-launcher/TorDomainIsolator.sys.mjs
=====================================
@@ -12,6 +12,11 @@ import {
const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+ TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+});
+
XPCOMUtils.defineLazyServiceGetters(lazy, {
ProtocolProxyService: [
"@mozilla.org/network/protocol-proxy-service;1",
@@ -19,11 +24,6 @@ XPCOMUtils.defineLazyServiceGetters(lazy, {
],
});
-ChromeUtils.defineESModuleGetters(lazy, {
- TorMonitorTopics: "resource://gre/modules/TorMonitorService.sys.mjs",
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
-});
-
const logger = new ConsoleAPI({
prefix: "TorDomainIsolator",
maxLogLevel: "warn",
@@ -143,7 +143,7 @@ class TorDomainIsolatorImpl {
Services.prefs.addObserver(NON_TOR_PROXY_PREF, this);
Services.obs.addObserver(this, NEW_IDENTITY_TOPIC);
- Services.obs.addObserver(this, lazy.TorMonitorTopics.StreamSucceeded);
+ Services.obs.addObserver(this, lazy.TorProviderTopics.StreamSucceeded);
this.#cleanupIntervalId = setInterval(
this.#clearKnownCircuits.bind(this),
@@ -158,7 +158,7 @@ class TorDomainIsolatorImpl {
uninit() {
Services.prefs.removeObserver(NON_TOR_PROXY_PREF, this);
Services.obs.removeObserver(this, NEW_IDENTITY_TOPIC);
- Services.obs.removeObserver(this, lazy.TorMonitorTopics.StreamSucceeded);
+ Services.obs.removeObserver(this, lazy.TorProviderTopics.StreamSucceeded);
clearInterval(this.#cleanupIntervalId);
this.#cleanupIntervalId = null;
this.clearIsolation();
@@ -257,12 +257,12 @@ class TorDomainIsolatorImpl {
);
this.clearIsolation();
try {
- await lazy.TorProtocolService.newnym();
+ await lazy.TorProviderBuilder.build().newnym();
} catch (e) {
logger.error("Could not send the newnym command", e);
// TODO: What UX to use here? See tor-browser#41708
}
- } else if (topic === lazy.TorMonitorTopics.StreamSucceeded) {
+ } else if (topic === lazy.TorProviderTopics.StreamSucceeded) {
const { username, password, circuit } = subject.wrappedJSObject;
this.#updateCircuit(username, password, circuit);
}
@@ -553,7 +553,7 @@ class TorDomainIsolatorImpl {
data = await Promise.all(
circuit.map(fingerprint =>
- lazy.TorProtocolService.getNodeInfo(fingerprint)
+ lazy.TorProviderBuilder.build().getNodeInfo(fingerprint)
)
);
this.#knownCircuits.set(id, data);
=====================================
toolkit/components/tor-launcher/TorMonitorService.sys.mjs deleted
=====================================
@@ -1,42 +0,0 @@
-// Copyright (c) 2022, The Tor Project, Inc.
-
-import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
-
-const lazy = {};
-ChromeUtils.defineESModuleGetters(lazy, {
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
-});
-
-export const TorMonitorTopics = Object.freeze({
- BridgeChanged: TorProviderTopics.BridgeChanged,
- StreamSucceeded: TorProviderTopics.StreamSucceeded,
-});
-
-/**
- * This service monitors an existing Tor instance, or starts one, if needed, and
- * then starts monitoring it.
- *
- * This is the service which should be queried to know information about the
- * status of the bootstrap, the logs, etc...
- */
-export const TorMonitorService = {
- get currentBridge() {
- return lazy.TorProtocolService.currentBridge;
- },
-
- get ownsTorDaemon() {
- return lazy.TorProtocolService.ownsTorDaemon;
- },
-
- get isRunning() {
- return lazy.TorProtocolService.isRunning;
- },
-
- get isBootstrapDone() {
- return lazy.TorProtocolService.isBootstrapDone;
- },
-
- getLog() {
- return lazy.TorProtocolService.getLog();
- },
-};
=====================================
toolkit/components/tor-launcher/TorParsers.sys.mjs
=====================================
@@ -269,11 +269,14 @@ export const TorParsers = Object.freeze({
},
parseBridgeLine(line) {
+ if (!line) {
+ return null;
+ }
const re =
/\s*(?:(?<transport>\S+)\s+)?(?<addr>[0-9a-fA-F\.\[\]\:]+:\d{1,5})(?:\s+(?<id>[0-9a-fA-F]{40}))?(?:\s+(?<args>.+))?/;
const match = re.exec(line);
if (!match) {
- throw new Error("Invalid bridge line.");
+ throw new Error(`Invalid bridge line: ${line}.`);
}
const bridge = match.groups;
if (!bridge.transport) {
=====================================
toolkit/components/tor-launcher/TorProtocolService.sys.mjs → toolkit/components/tor-launcher/TorProvider.sys.mjs
=====================================
@@ -1,4 +1,6 @@
-// Copyright (c) 2021, The Tor Project, Inc.
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
@@ -11,7 +13,6 @@ import {
import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
const lazy = {};
-
ChromeUtils.defineESModuleGetters(lazy, {
controller: "resource://gre/modules/TorControlPort.sys.mjs",
configureControlPortModule: "resource://gre/modules/TorControlPort.sys.mjs",
@@ -21,7 +22,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
const logger = new ConsoleAPI({
maxLogLevel: "warn",
- prefix: "TorProtocolService",
+ maxLogLevelPref: "browser.tor_provider.log_level",
+ prefix: "TorProvider",
});
/**
@@ -70,7 +72,7 @@ const ControlConnTimings = Object.freeze({
* It can start a new tor instance, or connect to an existing one.
* In the former case, it also takes its ownership by default.
*/
-class TorProvider {
+export class TorProvider {
#inited = false;
// Maintain a map of tor settings set by Tor Browser so that we don't
@@ -85,7 +87,6 @@ class TorProvider {
#SOCKSPortInfo = null; // An object that contains ipcFile, host, port.
#controlConnection = null; // This is cached and reused.
- #connectionQueue = [];
// Public methods
@@ -123,39 +124,34 @@ class TorProvider {
// takes a Map containing tor settings
// throws on error
async writeSettings(aSettingsObj) {
+ const entries =
+ aSettingsObj instanceof Map
+ ? Array.from(aSettingsObj.entries())
+ : Object.entries(aSettingsObj);
// only write settings that have changed
- const newSettings = Array.from(aSettingsObj).filter(([setting, value]) => {
- // make sure we have valid data here
- this.#assertValidSetting(setting, value);
-
+ const newSettings = entries.filter(([setting, value]) => {
if (!this.#settingsCache.has(setting)) {
// no cached setting, so write
return true;
}
const cachedValue = this.#settingsCache.get(setting);
- if (value === cachedValue) {
- return false;
- } else if (Array.isArray(value) && Array.isArray(cachedValue)) {
- // compare arrays member-wise
- if (value.length !== cachedValue.length) {
- return true;
- }
- for (let i = 0; i < value.length; i++) {
- if (value[i] !== cachedValue[i]) {
- return true;
- }
- }
- return false;
+ // Arrays are the only special case for which === could fail.
+ // The other values we accept (strings, booleans, numbers, null and
+ // undefined) work correctly with ===.
+ if (Array.isArray(value) && Array.isArray(cachedValue)) {
+ return (
+ value.length !== cachedValue.length ||
+ value.some((val, idx) => val !== cachedValue[idx])
+ );
}
- // some other different values
- return true;
+ return value !== cachedValue;
});
// only write if new setting to save
if (newSettings.length) {
- const settingsObject = Object.fromEntries(newSettings);
- await this.setConfWithReply(settingsObject);
+ const conn = await this.#getConnection();
+ await conn.setConf(Object.fromEntries(newSettings));
// save settings to cache after successfully writing to Tor
for (const [setting, value] of newSettings) {
@@ -164,23 +160,15 @@ class TorProvider {
}
}
- async readStringArraySetting(aSetting) {
- const value = await this.#readSetting(aSetting);
- this.#settingsCache.set(aSetting, value);
- return value;
- }
-
// writes current tor settings to disk
async flushSettings() {
- await this.sendCommand("SAVECONF");
+ const conn = await this.#getConnection();
+ await conn.flushSettings();
}
async connect() {
- const kTorConfKeyDisableNetwork = "DisableNetwork";
- const settings = {};
- settings[kTorConfKeyDisableNetwork] = false;
- await this.setConfWithReply(settings);
- await this.sendCommand("SAVECONF");
+ const conn = await this.#getConnection();
+ await conn.setNetworkEnabled(true);
this.clearBootstrapError();
this.retrieveBootstrapStatus();
}
@@ -188,12 +176,8 @@ class TorProvider {
async stopBootstrap() {
// Tell tor to disable use of the network; this should stop the bootstrap
// process.
- try {
- const settings = { DisableNetwork: true };
- await this.setConfWithReply(settings);
- } catch (e) {
- logger.error("Error stopping bootstrap", e);
- }
+ const conn = await this.#getConnection();
+ await conn.setNetworkEnabled(false);
// We are not interested in waiting for this, nor in **catching its error**,
// so we do not await this. We just want to be notified when the bootstrap
// status is actually updated through observers.
@@ -201,28 +185,31 @@ class TorProvider {
}
async newnym() {
- return this.sendCommand("SIGNAL NEWNYM");
+ const conn = await this.#getConnection();
+ await conn.newnym();
}
// Ask tor which ports it is listening to for SOCKS connections.
// At the moment this is used only in TorCheckService.
async getSocksListeners() {
- const cmd = "GETINFO";
- const keyword = "net/listeners/socks";
- const response = await this.sendCommand(cmd, keyword);
- return TorParsers.parseReply(cmd, keyword, response);
+ const conn = await this.#getConnection();
+ return conn.getSocksListeners();
}
async getBridges() {
+ const conn = await this.#getConnection();
// Ideally, we would not need this function, because we should be the one
// setting them with TorSettings. However, TorSettings is not notified of
// change of settings. So, asking tor directly with the control connection
// is the most reliable way of getting the configured bridges, at the
// moment. Also, we are using this for the circuit display, which should
// work also when we are not configuring the tor daemon, but just using it.
- return this.#withConnection(conn => {
- return conn.getConf("bridge");
- });
+ return conn.getBridges();
+ }
+
+ async getPluggableTransports() {
+ const conn = await this.#getConnection();
+ return conn.getPluggableTransports();
}
/**
@@ -232,68 +219,55 @@ class TorProvider {
* @returns {Promise<NodeData>}
*/
async getNodeInfo(id) {
- return this.#withConnection(async conn => {
- const node = {
- fingerprint: id,
- ipAddrs: [],
- bridgeType: null,
- regionCode: null,
- };
- const bridge = (await conn.getConf("bridge"))?.find(
- foundBridge => foundBridge.ID?.toUpperCase() === id.toUpperCase()
- );
- const addrRe = /^\[?([^\]]+)\]?:\d+$/;
- if (bridge) {
- node.bridgeType = bridge.type ?? "";
- // Attempt to get an IP address from bridge address string.
- const ip = bridge.address.match(addrRe)?.[1];
- if (ip && !ip.startsWith("0.")) {
- node.ipAddrs.push(ip);
- }
- } else {
- // Either dealing with a relay, or a bridge whose fingerprint is not
- // saved in torrc.
- const info = await conn.getInfo(`ns/id/${id}`);
- if (info.IP && !info.IP.startsWith("0.")) {
- node.ipAddrs.push(info.IP);
- }
- const ip6 = info.IPv6?.match(addrRe)?.[1];
- if (ip6) {
- node.ipAddrs.push(ip6);
- }
+ const conn = await this.#getConnection();
+ const node = {
+ fingerprint: id,
+ ipAddrs: [],
+ bridgeType: null,
+ regionCode: null,
+ };
+ const bridge = (await conn.getBridges())?.find(
+ foundBridge => foundBridge.id?.toUpperCase() === id.toUpperCase()
+ );
+ if (bridge) {
+ node.bridgeType = bridge.transport ?? "";
+ // Attempt to get an IP address from bridge address string.
+ const ip = bridge.addr.match(/^\[?([^\]]+)\]?:\d+$/)?.[1];
+ if (ip && !ip.startsWith("0.")) {
+ node.ipAddrs.push(ip);
}
- if (node.ipAddrs.length) {
- // Get the country code for the node's IP address.
- let regionCode;
- try {
- // Expect a 2-letter ISO3166-1 code, which should also be a valid
- // BCP47 Region subtag.
- regionCode = await conn.getInfo("ip-to-country/" + node.ipAddrs[0]);
- } catch {}
+ } else {
+ node.ipAddrs = await conn.getNodeAddresses(id);
+ }
+ if (node.ipAddrs.length) {
+ // Get the country code for the node's IP address.
+ try {
+ // Expect a 2-letter ISO3166-1 code, which should also be a valid
+ // BCP47 Region subtag.
+ const regionCode = await conn.getIPCountry(node.ipAddrs[0]);
if (regionCode && regionCode !== "??") {
node.regionCode = regionCode.toUpperCase();
}
+ } catch (e) {
+ logger.warn(`Cannot get a country for IP ${node.ipAddrs[0]}`, e);
}
- return node;
- });
+ }
+ return node;
}
- async onionAuthAdd(hsAddress, b64PrivateKey, isPermanent) {
- return this.#withConnection(conn => {
- return conn.onionAuthAdd(hsAddress, b64PrivateKey, isPermanent);
- });
+ async onionAuthAdd(address, b64PrivateKey, isPermanent) {
+ const conn = await this.#getConnection();
+ return conn.onionAuthAdd(address, b64PrivateKey, isPermanent);
}
- async onionAuthRemove(hsAddress) {
- return this.#withConnection(conn => {
- return conn.onionAuthRemove(hsAddress);
- });
+ async onionAuthRemove(address) {
+ const conn = await this.#getConnection();
+ return conn.onionAuthRemove(address);
}
async onionAuthViewKeys() {
- return this.#withConnection(conn => {
- return conn.onionAuthViewKeys();
- });
+ const conn = await this.#getConnection();
+ return conn.onionAuthViewKeys();
}
// TODO: transform the following 4 functions in getters.
@@ -333,106 +307,6 @@ class TorProvider {
return this.#SOCKSPortInfo;
}
- // Public, but called only internally
-
- // Executes a command on the control port.
- // Return a reply object or null if a fatal error occurs.
- async sendCommand(cmd, args) {
- const maxTimeout = 1000;
- let leftConnAttempts = 5;
- let timeout = 250;
- let reply;
- while (leftConnAttempts-- > 0) {
- const response = await this.#trySend(cmd, args, leftConnAttempts === 0);
- if (response.connected) {
- reply = response.reply;
- break;
- }
- // We failed to acquire the controller after multiple attempts.
- // Try again after some time.
- logger.warn(
- "sendCommand: Acquiring control connection failed, trying again later.",
- cmd,
- args
- );
- await new Promise(resolve => setTimeout(() => resolve(), timeout));
- timeout = Math.min(2 * timeout, maxTimeout);
- }
-
- // We sent the command, but we still got an empty response.
- // Something must be busted elsewhere.
- if (!reply) {
- throw new Error(`${cmd} sent an empty response`);
- }
-
- // TODO: Move the parsing of the reply to the controller, because anyone
- // calling sendCommand on it actually wants a parsed reply.
-
- reply = TorParsers.parseCommandResponse(reply);
- if (!TorParsers.commandSucceeded(reply)) {
- if (reply?.lineArray) {
- throw new Error(reply.lineArray.join("\n"));
- }
- throw new Error(`${cmd} failed with code ${reply.statusCode}`);
- }
-
- return reply;
- }
-
- // Perform a SETCONF command.
- // aSettingsObj should be a JavaScript object with keys (property values)
- // that correspond to tor config. keys. The value associated with each
- // key should be a simple string, a string array, or a Boolean value.
- // If an associated value is undefined or null, a key with no value is
- // passed in the SETCONF command.
- // Throws in case of error, or returns a reply object.
- async setConfWithReply(settings) {
- if (!settings) {
- throw new Error("Empty settings object");
- }
- const args = Object.entries(settings)
- .map(([key, val]) => {
- if (val === undefined || val === null) {
- return key;
- }
- const valType = typeof val;
- let rv = `${key}=`;
- if (valType === "boolean") {
- rv += val ? "1" : "0";
- } else if (Array.isArray(val)) {
- rv += val.map(TorParsers.escapeString).join(` ${key}=`);
- } else if (valType === "string") {
- rv += TorParsers.escapeString(val);
- } else {
- logger.error(`Got unsupported type for ${key}`, val);
- throw new Error(`Unsupported type ${valType} (key ${key})`);
- }
- return rv;
- })
- .filter(arg => arg);
- if (!args.length) {
- throw new Error("No settings to set");
- }
-
- await this.sendCommand("SETCONF", args.join(" "));
- }
-
- // Public, never called?
-
- async readBoolSetting(aSetting) {
- let value = await this.#readBoolSetting(aSetting);
- this.#settingsCache.set(aSetting, value);
- return value;
- }
-
- async readStringSetting(aSetting) {
- let value = await this.#readStringSetting(aSetting);
- this.#settingsCache.set(aSetting, value);
- return value;
- }
-
- // Private
-
async #setSockets() {
try {
const isWindows = TorLauncherUtil.isWindows;
@@ -511,167 +385,24 @@ class TorProvider {
}
}
- #assertValidSettingKey(aSetting) {
- // ensure the 'key' is a string
- if (typeof aSetting !== "string") {
- throw new Error(
- `Expected setting of type string but received ${typeof aSetting}`
- );
- }
- }
-
- #assertValidSetting(aSetting, aValue) {
- this.#assertValidSettingKey(aSetting);
- switch (typeof aValue) {
- case "boolean":
- case "string":
- return;
- case "object":
- if (aValue === null) {
- return;
- } else if (Array.isArray(aValue)) {
- for (const element of aValue) {
- if (typeof element !== "string") {
- throw new Error(
- `Setting '${aSetting}' array contains value of invalid type '${typeof element}'`
- );
- }
- }
- return;
- }
- // fall through
- default:
- throw new Error(
- `Invalid object type received for setting '${aSetting}'`
- );
- }
- }
-
- // Perform a GETCONF command.
- async #readSetting(aSetting) {
- this.#assertValidSettingKey(aSetting);
-
- const cmd = "GETCONF";
- let reply = await this.sendCommand(cmd, aSetting);
- return TorParsers.parseReply(cmd, aSetting, reply);
- }
-
- async #readStringSetting(aSetting) {
- let lineArray = await this.#readSetting(aSetting);
- if (lineArray.length !== 1) {
- throw new Error(
- `Expected an array with length 1 but received array of length ${lineArray.length}`
- );
- }
- return lineArray[0];
- }
-
- async #readBoolSetting(aSetting) {
- const value = this.#readStringSetting(aSetting);
- switch (value) {
- case "0":
- return false;
- case "1":
- return true;
- default:
- throw new Error(`Expected boolean (1 or 0) but received '${value}'`);
- }
- }
-
- async #trySend(cmd, args, rethrow) {
- let connected = false;
- let reply;
- let leftAttempts = 2;
- while (leftAttempts-- > 0) {
- let conn;
- try {
- conn = await this.#getConnection();
- } catch (e) {
- logger.error("Cannot get a connection to the control port", e);
- if (leftAttempts == 0 && rethrow) {
- throw e;
- }
- }
- if (!conn) {
- continue;
- }
- // If we _ever_ got a connection, the caller should not try again
- connected = true;
- try {
- reply = await conn.sendCommand(cmd + (args ? " " + args : ""));
- if (reply) {
- // Return for reuse.
- this.#returnConnection();
- } else {
- // Connection is bad.
- logger.warn(
- "sendCommand returned an empty response, taking the connection as broken and closing it."
- );
- this.#closeConnection();
- }
- } catch (e) {
- logger.error(`Cannot send the command ${cmd}`, e);
- this.#closeConnection();
- if (leftAttempts == 0 && rethrow) {
- throw e;
- }
- }
- }
- return { connected, reply };
- }
-
- // Opens an authenticated connection, sets it to this.#controlConnection, and
- // return it.
async #getConnection() {
- if (!this.#controlConnection) {
+ if (!this.#controlConnection?.isOpen) {
this.#controlConnection = await lazy.controller();
}
- if (this.#controlConnection.inUse) {
- await new Promise((resolve, reject) =>
- this.#connectionQueue.push({ resolve, reject })
- );
- } else {
- this.#controlConnection.inUse = true;
- }
return this.#controlConnection;
}
- #returnConnection() {
- if (this.#connectionQueue.length) {
- this.#connectionQueue.shift().resolve();
- } else {
- this.#controlConnection.inUse = false;
- }
- }
-
- async #withConnection(func) {
- // TODO: Make more robust?
- const conn = await this.#getConnection();
- try {
- return await func(conn);
- } finally {
- this.#returnConnection();
- }
- }
-
- // If aConn is omitted, the cached connection is closed.
#closeConnection() {
if (this.#controlConnection) {
logger.info("Closing the control connection");
this.#controlConnection.close();
this.#controlConnection = null;
}
- for (const promise of this.#connectionQueue) {
- promise.reject("Connection closed");
- }
- this.#connectionQueue = [];
}
async #reconnect() {
this.#closeConnection();
- const conn = await this.#getConnection();
- logger.debug("Reconnected to the control port.");
- this.#returnConnection(conn);
+ await this.#getConnection();
}
async #readAuthenticationCookie(aPath) {
@@ -777,8 +508,9 @@ class TorProvider {
if (this.ownsTorDaemon) {
// When we own the tor daemon, we listen to more events, that are used
// for about:torconnect or for showing the logs in the settings page.
- this._eventHandlers.set("STATUS_CLIENT", (_eventType, lines) =>
- this._processBootstrapStatus(lines[0], false)
+ this._eventHandlers.set(
+ "STATUS_CLIENT",
+ this._processStatusClient.bind(this)
);
this._eventHandlers.set("NOTICE", this._processLog.bind(this));
this._eventHandlers.set("WARN", this._processLog.bind(this));
@@ -809,23 +541,10 @@ class TorProvider {
throw new Error("Event monitor connection not available");
}
- // TODO: Unify with TorProtocolService.sendCommand and put everything in the
- // reviewed torbutton replacement.
- const cmd = "GETINFO";
- const key = "status/bootstrap-phase";
- let reply = await this._connection.sendCommand(`${cmd} ${key}`);
-
- // A typical reply looks like:
- // 250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"
- // 250 OK
- reply = TorParsers.parseCommandResponse(reply);
- if (!TorParsers.commandSucceeded(reply)) {
- throw new Error(`${cmd} failed`);
- }
- reply = TorParsers.parseReply(cmd, key, reply);
- if (reply.length) {
- this._processBootstrapStatus(reply[0], true);
- }
+ this._processBootstrapStatus(
+ await this._connection.getBootstrapPhase(),
+ true
+ );
}
// Returns captured log message as a text string (one message per line).
@@ -1058,37 +777,32 @@ class TorProvider {
_monitorEvent(type, callback) {
logger.info(`Watching events of type ${type}.`);
let replyObj = {};
- this._connection.watchEvent(
- type,
- null,
- line => {
- if (!line) {
- return;
- }
- logger.debug("Event response: ", line);
- const isComplete = TorParsers.parseReplyLine(line, replyObj);
- if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
- return;
- }
- const reply = replyObj;
- replyObj = {};
- if (reply.statusCode !== TorStatuses.EventNotification) {
- logger.error("Unexpected event status code:", reply.statusCode);
- return;
- }
- if (!reply.lineArray[0].startsWith(`${type} `)) {
- logger.error("Wrong format for the first line:", reply.lineArray[0]);
- return;
- }
- reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
- try {
- callback(type, reply.lineArray);
- } catch (e) {
- logger.error("Exception while handling an event", reply, e);
- }
- },
- true
- );
+ this._connection.watchEvent(type, line => {
+ if (!line) {
+ return;
+ }
+ logger.debug("Event response: ", line);
+ const isComplete = TorParsers.parseReplyLine(line, replyObj);
+ if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
+ return;
+ }
+ const reply = replyObj;
+ replyObj = {};
+ if (reply.statusCode !== TorStatuses.EventNotification) {
+ logger.error("Unexpected event status code:", reply.statusCode);
+ return;
+ }
+ if (!reply.lineArray[0].startsWith(`${type} `)) {
+ logger.error("Wrong format for the first line:", reply.lineArray[0]);
+ return;
+ }
+ reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
+ try {
+ callback(type, reply.lineArray);
+ } catch (e) {
+ logger.error("Exception while handling an event", reply, e);
+ }
+ });
}
_processLog(type, lines) {
@@ -1116,15 +830,12 @@ class TorProvider {
// to TorBootstrapStatus observers.
// If aSuppressErrors is true, errors are ignored. This is used when we
// are handling the response to a "GETINFO status/bootstrap-phase" command.
- _processBootstrapStatus(aStatusMsg, aSuppressErrors) {
- const statusObj = TorParsers.parseBootstrapStatus(aStatusMsg);
- if (!statusObj) {
- return;
- }
-
+ _processBootstrapStatus(statusObj, suppressErrors) {
// Notify observers
- statusObj.wrappedJSObject = statusObj;
- Services.obs.notifyObservers(statusObj, "TorBootstrapStatus");
+ Services.obs.notifyObservers(
+ { wrappedJSObject: statusObj },
+ "TorBootstrapStatus"
+ );
if (statusObj.PROGRESS === 100) {
this._isBootstrapDone = true;
@@ -1141,7 +852,7 @@ class TorProvider {
if (
statusObj.TYPE === "WARN" &&
statusObj.RECOMMENDATION !== "ignore" &&
- !aSuppressErrors
+ !suppressErrors
) {
this._notifyBootstrapError(statusObj);
}
@@ -1184,6 +895,15 @@ class TorProvider {
}
}
+ _processStatusClient(_type, lines) {
+ const statusObj = TorParsers.parseBootstrapStatus(lines[0]);
+ if (!statusObj) {
+ // No `BOOTSTRAP` in the line
+ return;
+ }
+ this._processBootstrapStatus(statusObj, false);
+ }
+
async _processCircEvent(_type, lines) {
const builtEvent =
/^(?<CircuitID>[a-zA-Z0-9]{1,16})\sBUILT\s(?<Path>(?:,?\$[0-9a-fA-F]{40}(?:~[a-zA-Z0-9]{1,19})?)+)/.exec(
@@ -1295,7 +1015,3 @@ class TorProvider {
this.clearBootstrapError();
}
}
-
-// TODO: Stop defining TorProtocolService, make the builder instance the
-// TorProvider.
-export const TorProtocolService = new TorProvider();
=====================================
toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
=====================================
@@ -4,7 +4,7 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
+ TorProvider: "resource://gre/modules/TorProvider.sys.mjs",
});
export const TorProviderTopics = Object.freeze({
@@ -19,16 +19,25 @@ export const TorProviderTopics = Object.freeze({
});
export class TorProviderBuilder {
+ static #provider = null;
+
static async init() {
- await lazy.TorProtocolService.init();
+ const provider = new lazy.TorProvider();
+ await provider.init();
+ // Assign it only when initialization succeeds.
+ TorProviderBuilder.#provider = provider;
}
static uninit() {
- lazy.TorProtocolService.uninit();
+ TorProviderBuilder.#provider.uninit();
+ TorProviderBuilder.#provider = null;
}
// TODO: Switch to an async build?
static build() {
- return lazy.TorProtocolService;
+ if (!TorProviderBuilder.#provider) {
+ throw new Error("TorProviderBuilder has not been initialized yet.");
+ }
+ return TorProviderBuilder.#provider;
}
}
=====================================
toolkit/components/tor-launcher/TorStartupService.sys.mjs
=====================================
@@ -3,22 +3,13 @@ const lazy = {};
// We will use the modules only when the profile is loaded, so prefer lazy
// loading
ChromeUtils.defineESModuleGetters(lazy, {
+ TorConnect: "resource:///modules/TorConnect.sys.mjs",
TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
+ TorSettings: "resource:///modules/TorSettings.sys.mjs",
});
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorConnect",
- "resource:///modules/TorConnect.jsm"
-);
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorSettings",
- "resource:///modules/TorSettings.jsm"
-);
-
/* Browser observer topis */
const BrowserTopics = Object.freeze({
ProfileAfterChange: "profile-after-change",
=====================================
toolkit/components/tor-launcher/moz.build
=====================================
@@ -3,10 +3,9 @@ EXTRA_JS_MODULES += [
"TorControlPort.sys.mjs",
"TorDomainIsolator.sys.mjs",
"TorLauncherUtil.sys.mjs",
- "TorMonitorService.sys.mjs",
"TorParsers.sys.mjs",
"TorProcess.sys.mjs",
- "TorProtocolService.sys.mjs",
+ "TorProvider.sys.mjs",
"TorProviderBuilder.sys.mjs",
"TorStartupService.sys.mjs",
]
=====================================
toolkit/mozapps/update/UpdateService.sys.mjs
=====================================
@@ -23,18 +23,13 @@ ChromeUtils.defineESModuleGetters(lazy, {
AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
CertUtils: "resource://gre/modules/CertUtils.sys.mjs",
DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
ctypes: "resource://gre/modules/ctypes.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
});
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorMonitorService",
- "resource://gre/modules/TorMonitorService.jsm"
-);
-
XPCOMUtils.defineLazyServiceGetter(
lazy,
"AUS",
@@ -394,10 +389,11 @@ XPCOMUtils.defineLazyGetter(
);
function _shouldRegisterBootstrapObserver(errorCode) {
+ const provider = lazy.TorProviderBuilder.build();
return (
errorCode == PROXY_SERVER_CONNECTION_REFUSED &&
- !lazy.TorMonitorService.isBootstrapDone &&
- lazy.TorMonitorService.ownsTorDaemon
+ !provider.isBootstrapDone &&
+ provider.ownsTorDaemon
);
}
@@ -5833,10 +5829,7 @@ Downloader.prototype = {
// we choose to compute these hashes.
hash = hash.finish(false);
digest = Array.from(hash, (c, i) =>
- hash
- .charCodeAt(i)
- .toString(16)
- .padStart(2, "0")
+ hash.charCodeAt(i).toString(16).padStart(2, "0")
).join("");
} catch (e) {
LOG(
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/ab8a15…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/ab8a15…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] 3 commits: Bug 40855: Update toolchains for Firefox 115 (Android part)
by Pier Angelo Vendrame (@pierov) 07 Aug '23
by Pier Angelo Vendrame (@pierov) 07 Aug '23
07 Aug '23
Pier Angelo Vendrame pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
a65bfce8 by Pier Angelo Vendrame at 2023-08-07T17:44:54+02:00
Bug 40855: Update toolchains for Firefox 115 (Android part)
Android-only toolchain updates.
- - - - -
301a540c by Pier Angelo Vendrame at 2023-08-07T17:44:56+02:00
Bug 40855: Updates for Firefox 115 (Application Services)
Application services needs NSS and SQLCipher.
We had two projects for them, but they are used only by AS.
So, our build scripts were a copy of Mozilla's, and we applied the same
patches.
This meant we needed to keep the build scripts up to date, with all the
additional changes for RBM.
Since no other project depended on them, we can build these libraries
here with Mozilla's scripts, without the need to keep theirs and ours
in sync.
In addition to that, this commit updates the list of Java dependencies.
- - - - -
b7d80c1d by Pier Angelo Vendrame at 2023-08-07T17:44:56+02:00
Bug 40867: Add a project for firefox-android.
This project replaces Android Components and Fenix.
- - - - -
13 changed files:
- Makefile
- − projects/android-components/build
- − projects/android-components/config
- − projects/android-components/gradle-dependencies-list.txt
- − projects/android-components/list_toolchain_updates_checks
- − projects/android-components/mavenLocal.patch
- projects/android-toolchain/build
- projects/android-toolchain/config
- + projects/application-services/apply-bug-13028.diff
- projects/application-services/bug40485.patch → projects/application-services/bug40485.diff
- projects/nss/bug_13028.patch → projects/application-services/bug_13028.patch
- projects/application-services/build
- projects/application-services/config
The diff was not included because it is too large.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/compare/…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/compare/…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 40880 (fix): Add zstd back again
by Pier Angelo Vendrame (@pierov) 07 Aug '23
by Pier Angelo Vendrame (@pierov) 07 Aug '23
07 Aug '23
Pier Angelo Vendrame pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
d6d756ac by Pier Angelo Vendrame at 2023-08-04T08:19:45+02:00
Bug 40880 (fix): Add zstd back again
The previous commit removed zstd from the list of the dependencies in
the README.
This commit restores it and sorts the dependencies.
- - - - -
1 changed file:
- README
Changes:
=====================================
README
=====================================
@@ -39,14 +39,14 @@ You also need a few perl modules installed:
If you are running Debian or Ubuntu, you can install them with:
-# apt-get install libyaml-libyaml-perl libtemplate-perl libdatetime-perl \
- libio-handle-util-perl libio-all-perl \
- libio-captureoutput-perl libjson-perl libpath-tiny-perl \
- libstring-shellquote-perl libsort-versions-perl \
- libdigest-sha-perl libdata-uuid-perl libdata-dump-perl \
- libfile-copy-recursive-perl libfile-slurp-perl git \
- mercurial uidmap libxml-writer-perl \
- libparallel-forkmanager-perl libxml-libxml-perl
+# apt-get install libdata-dump-perl libdata-uuid-perl libdatetime-perl \
+ libdigest-sha-perl libfile-copy-recursive-perl \
+ libfile-slurp-perl libio-all-perl libio-captureoutput-perl \
+ libio-handle-util-perl libjson-perl \
+ libparallel-forkmanager-perl libpath-tiny-perl \
+ libsort-versions-perl libstring-shellquote-perl \
+ libtemplate-perl libxml-libxml-perl libxml-writer-perl \
+ libyaml-libyaml-perl git mercurial uidmap zstd
If you are running an Arch based system, you should be able to install them with:
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/d…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/d…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.1.0esr-13.0-1] 2 commits: fixup! Bug 40933: Add tor-launcher functionality
by Pier Angelo Vendrame (@pierov) 05 Aug '23
by Pier Angelo Vendrame (@pierov) 05 Aug '23
05 Aug '23
Pier Angelo Vendrame pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
d5283d94 by Pier Angelo Vendrame at 2023-08-04T20:03:25+02:00
fixup! Bug 40933: Add tor-launcher functionality
Make TorProtocolService an ES class, and change _ with actual private
stuff.
- - - - -
ab8a15b7 by Pier Angelo Vendrame at 2023-08-04T20:03:26+02:00
fixup! Bug 40933: Add tor-launcher functionality
Merged TorMonitorService into TorProtocolService.
- - - - -
5 changed files:
- toolkit/components/tor-launcher/TorMonitorService.sys.mjs
- toolkit/components/tor-launcher/TorProtocolService.sys.mjs
- + toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
- toolkit/components/tor-launcher/TorStartupService.sys.mjs
- toolkit/components/tor-launcher/moz.build
Changes:
=====================================
toolkit/components/tor-launcher/TorMonitorService.sys.mjs
=====================================
@@ -1,73 +1,17 @@
// Copyright (c) 2022, The Tor Project, Inc.
-import { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs";
-import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
-
-import {
- TorParsers,
- TorStatuses,
-} from "resource://gre/modules/TorParsers.sys.mjs";
-import { TorProcess } from "resource://gre/modules/TorProcess.sys.mjs";
-
-import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs";
+import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
const lazy = {};
-
-ChromeUtils.defineESModuleGetters(lazy, {
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
- controller: "resource://gre/modules/TorControlPort.sys.mjs",
-});
-
ChromeUtils.defineESModuleGetters(lazy, {
TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
});
-const logger = new ConsoleAPI({
- maxLogLevel: "warn",
- maxLogLevelPref: "browser.tor_monitor_service.log_level",
- prefix: "TorMonitorService",
-});
-
-const Preferences = Object.freeze({
- PromptAtStartup: "extensions.torlauncher.prompt_at_startup",
-});
-
-const TorTopics = Object.freeze({
- BootstrapError: "TorBootstrapError",
- HasWarnOrErr: "TorLogHasWarnOrErr",
- ProcessExited: "TorProcessExited",
- ProcessIsReady: "TorProcessIsReady",
- ProcessRestarted: "TorProcessRestarted",
-});
-
export const TorMonitorTopics = Object.freeze({
- BridgeChanged: "TorBridgeChanged",
- StreamSucceeded: "TorStreamSucceeded",
-});
-
-const ControlConnTimings = Object.freeze({
- initialDelayMS: 25, // Wait 25ms after the process has started, before trying to connect
- maxRetryMS: 10000, // Retry at most every 10 seconds
- timeoutMS: 5 * 60 * 1000, // Wait at most 5 minutes for tor to start
+ BridgeChanged: TorProviderTopics.BridgeChanged,
+ StreamSucceeded: TorProviderTopics.StreamSucceeded,
});
-/**
- * From control-spec.txt:
- * CircuitID = 1*16 IDChar
- * IDChar = ALPHA / DIGIT
- * Currently, Tor only uses digits, but this may change.
- *
- * @typedef {string} CircuitID
- */
-/**
- * The fingerprint of a node.
- * From control-spec.txt:
- * Fingerprint = "$" 40*HEXDIG
- * However, we do not keep the $ in our structures.
- *
- * @typedef {string} NodeFingerprint
- */
-
/**
* This service monitors an existing Tor instance, or starts one, if needed, and
* then starts monitoring it.
@@ -76,575 +20,23 @@ const ControlConnTimings = Object.freeze({
* status of the bootstrap, the logs, etc...
*/
export const TorMonitorService = {
- _connection: null,
- _eventHandlers: {},
- _torLog: [], // Array of objects with date, type, and msg properties.
- _startTimeout: null,
-
- _isBootstrapDone: false,
- _lastWarningPhase: null,
- _lastWarningReason: null,
-
- _torProcess: null,
-
- _inited: false,
-
- /**
- * Stores the nodes of a circuit. Keys are cicuit IDs, and values are the node
- * fingerprints.
- *
- * Theoretically, we could hook this map up to the new identity notification,
- * but in practice it does not work. Tor pre-builds circuits, and the NEWNYM
- * signal does not affect them. So, we might end up using a circuit that was
- * 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[]>}
- */
- _circuits: new Map(),
- /**
- * The last used bridge, or null if bridges are not in use or if it was not
- * possible to detect the bridge. This needs the user to have specified bridge
- * lines with fingerprints to work.
- *
- * @type {NodeFingerprint?}
- */
- _currentBridge: null,
-
- // Public methods
-
- // Starts Tor, if needed, and starts monitoring for events
- init() {
- if (this._inited) {
- return;
- }
- this._inited = true;
-
- // We always liten to these events, because they are needed for the circuit
- // display.
- this._eventHandlers = new Map([
- ["CIRC", this._processCircEvent.bind(this)],
- ["STREAM", this._processStreamEvent.bind(this)],
- ]);
-
- if (this.ownsTorDaemon) {
- // When we own the tor daemon, we listen to more events, that are used
- // for about:torconnect or for showing the logs in the settings page.
- this._eventHandlers.set("STATUS_CLIENT", (_eventType, lines) =>
- this._processBootstrapStatus(lines[0], false)
- );
- this._eventHandlers.set("NOTICE", this._processLog.bind(this));
- this._eventHandlers.set("WARN", this._processLog.bind(this));
- this._eventHandlers.set("ERR", this._processLog.bind(this));
- this._controlTor();
- } else {
- this._startEventMonitor();
- }
- logger.info("TorMonitorService initialized");
- },
-
- // Closes the connection that monitors for events.
- // When Tor is started by Tor Browser, it is configured to exit when the
- // control connection is closed. Therefore, as a matter of facts, calling this
- // function also makes the child Tor instance stop.
- uninit() {
- if (this._torProcess) {
- this._torProcess.forget();
- this._torProcess.onExit = null;
- this._torProcess.onRestart = null;
- this._torProcess = null;
- }
- this._shutDownEventMonitor();
- },
-
- async retrieveBootstrapStatus() {
- if (!this._connection) {
- throw new Error("Event monitor connection not available");
- }
-
- // TODO: Unify with TorProtocolService.sendCommand and put everything in the
- // reviewed torbutton replacement.
- const cmd = "GETINFO";
- const key = "status/bootstrap-phase";
- let reply = await this._connection.sendCommand(`${cmd} ${key}`);
-
- // A typical reply looks like:
- // 250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"
- // 250 OK
- reply = TorParsers.parseCommandResponse(reply);
- if (!TorParsers.commandSucceeded(reply)) {
- throw new Error(`${cmd} failed`);
- }
- reply = TorParsers.parseReply(cmd, key, reply);
- if (reply.length) {
- this._processBootstrapStatus(reply[0], true);
- }
- },
-
- // Returns captured log message as a text string (one message per line).
- getLog() {
- return this._torLog
- .map(logObj => {
- const timeStr = logObj.date
- .toISOString()
- .replace("T", " ")
- .replace("Z", "");
- return `${timeStr} [${logObj.type}] ${logObj.msg}`;
- })
- .join(TorLauncherUtil.isWindows ? "\r\n" : "\n");
+ get currentBridge() {
+ return lazy.TorProtocolService.currentBridge;
},
- // true if we launched and control tor, false if using system tor
get ownsTorDaemon() {
- return TorLauncherUtil.shouldStartAndOwnTor;
- },
-
- get isBootstrapDone() {
- return this._isBootstrapDone;
- },
-
- clearBootstrapError() {
- this._lastWarningPhase = null;
- this._lastWarningReason = null;
+ return lazy.TorProtocolService.ownsTorDaemon;
},
get isRunning() {
- return !!this._connection;
+ return lazy.TorProtocolService.isRunning;
},
- /**
- * Return the data about the current bridge, if any, or null.
- * We can detect bridge only when the configured bridge lines include the
- * fingerprints.
- *
- * @returns {NodeData?} The node information, or null if the first node
- * is not a bridge, or no circuit has been opened, yet.
- */
- get currentBridge() {
- return this._currentBridge;
- },
-
- // Private methods
-
- async _startProcess() {
- // TorProcess should be instanced once, then always reused and restarted
- // only through the prompt it exposes when the controlled process dies.
- if (!this._torProcess) {
- this._torProcess = new TorProcess(
- lazy.TorProtocolService.torControlPortInfo,
- lazy.TorProtocolService.torSOCKSPortInfo
- );
- this._torProcess.onExit = () => {
- this._shutDownEventMonitor();
- Services.obs.notifyObservers(null, TorTopics.ProcessExited);
- };
- this._torProcess.onRestart = async () => {
- this._shutDownEventMonitor();
- await this._controlTor();
- Services.obs.notifyObservers(null, TorTopics.ProcessRestarted);
- };
- }
-
- // Already running, but we did not start it
- if (this._torProcess.isRunning) {
- return false;
- }
-
- try {
- await this._torProcess.start();
- if (this._torProcess.isRunning) {
- logger.info("tor started");
- this._torProcessStartTime = Date.now();
- }
- } catch (e) {
- // TorProcess already logs the error.
- this._lastWarningPhase = "startup";
- this._lastWarningReason = e.toString();
- }
- return this._torProcess.isRunning;
- },
-
- async _controlTor() {
- if (!this._torProcess?.isRunning && !(await this._startProcess())) {
- logger.error("Tor not running, not starting to monitor it.");
- return;
- }
-
- let delayMS = ControlConnTimings.initialDelayMS;
- const callback = async () => {
- if (await this._startEventMonitor()) {
- this.retrieveBootstrapStatus().catch(e => {
- logger.warn("Could not get the initial bootstrap status", e);
- });
-
- // FIXME: TorProcess is misleading here. We should use a topic related
- // to having a control port connection, instead.
- logger.info(`Notifying ${TorTopics.ProcessIsReady}`);
- Services.obs.notifyObservers(null, TorTopics.ProcessIsReady);
-
- // We reset this here hoping that _shutDownEventMonitor can interrupt
- // the current monitor, either by calling clearTimeout and preventing it
- // from starting, or by closing the control port connection.
- if (this._startTimeout === null) {
- logger.warn("Someone else reset _startTimeout!");
- }
- this._startTimeout = null;
- } else if (
- Date.now() - this._torProcessStartTime >
- ControlConnTimings.timeoutMS
- ) {
- let s = TorLauncherUtil.getLocalizedString("tor_controlconn_failed");
- this._lastWarningPhase = "startup";
- this._lastWarningReason = s;
- logger.info(s);
- if (this._startTimeout === null) {
- logger.warn("Someone else reset _startTimeout!");
- }
- this._startTimeout = null;
- } else {
- delayMS *= 2;
- if (delayMS > ControlConnTimings.maxRetryMS) {
- delayMS = ControlConnTimings.maxRetryMS;
- }
- this._startTimeout = setTimeout(() => {
- logger.debug(`Control port not ready, waiting ${delayMS / 1000}s.`);
- callback();
- }, delayMS);
- }
- };
- // Check again, in the unfortunate case in which the execution was alrady
- // queued, but was waiting network code.
- if (this._startTimeout === null) {
- this._startTimeout = setTimeout(callback, delayMS);
- } else {
- logger.error("Possible race? Refusing to start the timeout again");
- }
- },
-
- async _startEventMonitor() {
- if (this._connection) {
- return true;
- }
-
- let conn;
- try {
- conn = await lazy.controller();
- } catch (e) {
- logger.error("Cannot open a control port connection", e);
- if (conn) {
- try {
- conn.close();
- } catch (e) {
- logger.error(
- "Also, the connection is not null but cannot be closed",
- e
- );
- }
- }
- return false;
- }
-
- // TODO: optionally monitor INFO and DEBUG log messages.
- try {
- await conn.setEvents(Array.from(this._eventHandlers.keys()));
- } catch (e) {
- logger.error("SETEVENTS failed", e);
- conn.close();
- return false;
- }
-
- if (this._torProcess) {
- this._torProcess.connectionWorked();
- }
- if (this.ownsTorDaemon && !TorLauncherUtil.shouldOnlyConfigureTor) {
- try {
- await this._takeTorOwnership(conn);
- } catch (e) {
- logger.warn("Could not take ownership of the Tor daemon", e);
- }
- }
-
- this._connection = conn;
-
- for (const [type, callback] of this._eventHandlers.entries()) {
- this._monitorEvent(type, callback);
- }
-
- // Populate the circuit map already, in case we are connecting to an
- // external tor daemon.
- try {
- const reply = await this._connection.sendCommand(
- "GETINFO circuit-status"
- );
- const lines = reply.split(/\r?\n/);
- if (lines.shift() === "250+circuit-status=") {
- for (const line of lines) {
- if (line === ".") {
- break;
- }
- // _processCircEvent processes only one line at a time
- this._processCircEvent("CIRC", [line]);
- }
- }
- } catch (e) {
- logger.warn("Could not populate the initial circuit map", e);
- }
-
- return true;
- },
-
- // Try to become the primary controller (TAKEOWNERSHIP).
- async _takeTorOwnership(conn) {
- try {
- conn.takeOwnership();
- } catch (e) {
- logger.warn("Take ownership failed", e);
- return;
- }
- try {
- conn.resetOwningControllerProcess();
- } catch (e) {
- logger.warn("Clear owning controller process failed", e);
- }
- },
-
- _monitorEvent(type, callback) {
- logger.info(`Watching events of type ${type}.`);
- let replyObj = {};
- this._connection.watchEvent(
- type,
- null,
- line => {
- if (!line) {
- return;
- }
- logger.debug("Event response: ", line);
- const isComplete = TorParsers.parseReplyLine(line, replyObj);
- if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
- return;
- }
- const reply = replyObj;
- replyObj = {};
- if (reply.statusCode !== TorStatuses.EventNotification) {
- logger.error("Unexpected event status code:", reply.statusCode);
- return;
- }
- if (!reply.lineArray[0].startsWith(`${type} `)) {
- logger.error("Wrong format for the first line:", reply.lineArray[0]);
- return;
- }
- reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
- try {
- callback(type, reply.lineArray);
- } catch (e) {
- logger.error("Exception while handling an event", reply, e);
- }
- },
- true
- );
- },
-
- _processLog(type, lines) {
- if (type === "WARN" || type === "ERR") {
- // Notify so that Copy Log can be enabled.
- Services.obs.notifyObservers(null, TorTopics.HasWarnOrErr);
- }
-
- const date = new Date();
- const maxEntries = Services.prefs.getIntPref(
- "extensions.torlauncher.max_tor_log_entries",
- 1000
- );
- if (maxEntries > 0 && this._torLog.length >= maxEntries) {
- this._torLog.splice(0, 1);
- }
-
- const msg = lines.join("\n");
- this._torLog.push({ date, type, msg });
- const logString = `Tor ${type}: ${msg}`;
- logger.info(logString);
- },
-
- // Process a bootstrap status to update the current state, and broadcast it
- // to TorBootstrapStatus observers.
- // If aSuppressErrors is true, errors are ignored. This is used when we
- // are handling the response to a "GETINFO status/bootstrap-phase" command.
- _processBootstrapStatus(aStatusMsg, aSuppressErrors) {
- const statusObj = TorParsers.parseBootstrapStatus(aStatusMsg);
- if (!statusObj) {
- return;
- }
-
- // Notify observers
- statusObj.wrappedJSObject = statusObj;
- Services.obs.notifyObservers(statusObj, "TorBootstrapStatus");
-
- if (statusObj.PROGRESS === 100) {
- this._isBootstrapDone = true;
- try {
- Services.prefs.setBoolPref(Preferences.PromptAtStartup, false);
- } catch (e) {
- logger.warn(`Cannot set ${Preferences.PromptAtStartup}`, e);
- }
- return;
- }
-
- this._isBootstrapDone = false;
-
- if (
- statusObj.TYPE === "WARN" &&
- statusObj.RECOMMENDATION !== "ignore" &&
- !aSuppressErrors
- ) {
- this._notifyBootstrapError(statusObj);
- }
- },
-
- _notifyBootstrapError(statusObj) {
- try {
- Services.prefs.setBoolPref(Preferences.PromptAtStartup, true);
- } catch (e) {
- logger.warn(`Cannot set ${Preferences.PromptAtStartup}`, e);
- }
- const phase = TorLauncherUtil.getLocalizedBootstrapStatus(statusObj, "TAG");
- const reason = TorLauncherUtil.getLocalizedBootstrapStatus(
- statusObj,
- "REASON"
- );
- const details = TorLauncherUtil.getFormattedLocalizedString(
- "tor_bootstrap_failed_details",
- [phase, reason],
- 2
- );
- logger.error(
- `Tor bootstrap error: [${statusObj.TAG}/${statusObj.REASON}] ${details}`
- );
-
- if (
- statusObj.TAG !== this._lastWarningPhase ||
- statusObj.REASON !== this._lastWarningReason
- ) {
- this._lastWarningPhase = statusObj.TAG;
- this._lastWarningReason = statusObj.REASON;
-
- const message = TorLauncherUtil.getLocalizedString(
- "tor_bootstrap_failed"
- );
- Services.obs.notifyObservers(
- { message, details },
- TorTopics.BootstrapError
- );
- }
- },
-
- async _processCircEvent(_type, lines) {
- const builtEvent =
- /^(?<CircuitID>[a-zA-Z0-9]{1,16})\sBUILT\s(?<Path>(?:,?\$[0-9a-fA-F]{40}(?:~[a-zA-Z0-9]{1,19})?)+)/.exec(
- lines[0]
- );
- const closedEvent = /^(?<ID>[a-zA-Z0-9]{1,16})\sCLOSED/.exec(lines[0]);
- if (builtEvent) {
- const fp = /\$([0-9a-fA-F]{40})/g;
- const nodes = Array.from(builtEvent.groups.Path.matchAll(fp), g =>
- g[1].toUpperCase()
- );
- this._circuits.set(builtEvent.groups.CircuitID, nodes);
- // Ignore circuits of length 1, that are used, for example, to probe
- // bridges. So, only store them, since we might see streams that use them,
- // but then early-return.
- if (nodes.length === 1) {
- return;
- }
- // 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.
- // this._checkCredentials(lines[0], nodes);
- if (this._currentBridge?.fingerprint !== nodes[0]) {
- const nodeInfo = await lazy.TorProtocolService.getNodeInfo(nodes[0]);
- let notify = false;
- if (nodeInfo?.bridgeType) {
- logger.info(`Bridge changed to ${nodes[0]}`);
- this._currentBridge = nodeInfo;
- notify = true;
- } else if (this._currentBridge) {
- logger.info("Bridges disabled");
- this._currentBridge = null;
- notify = true;
- }
- if (notify) {
- Services.obs.notifyObservers(
- null,
- TorMonitorTopics.BridgeChanged,
- this._currentBridge
- );
- }
- }
- } else if (closedEvent) {
- this._circuits.delete(closedEvent.groups.ID);
- }
- },
-
- _processStreamEvent(_type, lines) {
- // The first block is the stream ID, which we do not need at the moment.
- const succeeedEvent =
- /^[a-zA-Z0-9]{1,16}\sSUCCEEDED\s(?<CircuitID>[a-zA-Z0-9]{1,16})/.exec(
- lines[0]
- );
- if (!succeeedEvent) {
- return;
- }
- const circuit = this._circuits.get(succeeedEvent.groups.CircuitID);
- if (!circuit) {
- logger.error(
- "Seen a STREAM SUCCEEDED with an unknown circuit. Not notifying observers.",
- lines[0]
- );
- return;
- }
- this._checkCredentials(lines[0], circuit);
- },
-
- /**
- * Check if a STREAM or CIRC response line contains SOCKS_USERNAME and
- * SOCKS_PASSWORD. In case, notify observers that we could associate a certain
- * circuit to these credentials.
- *
- * @param {string} line The circ or stream line to check
- * @param {NodeFingerprint[]} circuit The fingerprints of the nodes in the
- * circuit.
- */
- _checkCredentials(line, circuit) {
- const username = /SOCKS_USERNAME=("(?:[^"\\]|\\.)*")/.exec(line);
- const password = /SOCKS_PASSWORD=("(?:[^"\\]|\\.)*")/.exec(line);
- if (!username || !password) {
- return;
- }
- Services.obs.notifyObservers(
- {
- wrappedJSObject: {
- username: TorParsers.unescapeString(username[1]),
- password: TorParsers.unescapeString(password[1]),
- circuit,
- },
- },
- TorMonitorTopics.StreamSucceeded
- );
+ get isBootstrapDone() {
+ return lazy.TorProtocolService.isBootstrapDone;
},
- _shutDownEventMonitor() {
- try {
- this._connection?.close();
- } catch (e) {
- logger.error("Could not close the connection to the control port", e);
- }
- this._connection = null;
- if (this._startTimeout !== null) {
- clearTimeout(this._startTimeout);
- this._startTimeout = null;
- }
- this._isBootstrapDone = false;
- this.clearBootstrapError();
+ getLog() {
+ return lazy.TorProtocolService.getLog();
},
};
=====================================
toolkit/components/tor-launcher/TorProtocolService.sys.mjs
=====================================
@@ -1,32 +1,22 @@
// Copyright (c) 2021, The Tor Project, Inc.
-import { setTimeout } from "resource://gre/modules/Timer.sys.mjs";
+import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
-import { TorParsers } from "resource://gre/modules/TorParsers.sys.mjs";
import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs";
+import {
+ TorParsers,
+ TorStatuses,
+} from "resource://gre/modules/TorParsers.sys.mjs";
+import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
const lazy = {};
-ChromeUtils.defineModuleGetter(
- lazy,
- "FileUtils",
- "resource://gre/modules/FileUtils.jsm"
-);
-
-ChromeUtils.defineModuleGetter(
- lazy,
- "TorMonitorService",
- "resource://gre/modules/TorMonitorService.jsm"
-);
ChromeUtils.defineESModuleGetters(lazy, {
controller: "resource://gre/modules/TorControlPort.sys.mjs",
configureControlPortModule: "resource://gre/modules/TorControlPort.sys.mjs",
-});
-
-const TorTopics = Object.freeze({
- ProcessExited: "TorProcessExited",
- ProcessRestarted: "TorProcessRestarted",
+ FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
+ TorProcess: "resource://gre/modules/TorProcess.sys.mjs",
});
const logger = new ConsoleAPI({
@@ -34,11 +24,27 @@ const logger = new ConsoleAPI({
prefix: "TorProtocolService",
});
+/**
+ * From control-spec.txt:
+ * CircuitID = 1*16 IDChar
+ * IDChar = ALPHA / DIGIT
+ * Currently, Tor only uses digits, but this may change.
+ *
+ * @typedef {string} CircuitID
+ */
+/**
+ * The fingerprint of a node.
+ * From control-spec.txt:
+ * Fingerprint = "$" 40*HEXDIG
+ * However, we do not keep the $ in our structures.
+ *
+ * @typedef {string} NodeFingerprint
+ */
/**
* Stores the data associated with a circuit node.
*
* @typedef NodeData
- * @property {string} fingerprint The node fingerprint.
+ * @property {NodeFingerprint} fingerprint The node fingerprint.
* @property {string[]} ipAddrs - The ip addresses associated with this node.
* @property {string?} bridgeType - The bridge type for this node, or "" if the
* node is a bridge but the type is unknown, or null if this is not a bridge
@@ -48,60 +54,71 @@ const logger = new ConsoleAPI({
* valid BCP47 Region subtag.
*/
-// Manage the connection to tor's control port, to update its settings and query
-// other useful information.
-//
-// NOTE: Many Tor protocol functions return a reply object, which is a
-// a JavaScript object that has the following fields:
-// reply.statusCode -- integer, e.g., 250
-// reply.lineArray -- an array of strings returned by tor
-// For GetConf calls, the aKey prefix is removed from the lineArray strings.
-export const TorProtocolService = {
- _inited: false,
+const Preferences = Object.freeze({
+ PromptAtStartup: "extensions.torlauncher.prompt_at_startup",
+});
+
+const ControlConnTimings = Object.freeze({
+ initialDelayMS: 25, // Wait 25ms after the process has started, before trying to connect
+ maxRetryMS: 10000, // Retry at most every 10 seconds
+ timeoutMS: 5 * 60 * 1000, // Wait at most 5 minutes for tor to start
+});
+
+/**
+ * This is a Tor provider for the C Tor daemon.
+ *
+ * It can start a new tor instance, or connect to an existing one.
+ * In the former case, it also takes its ownership by default.
+ */
+class TorProvider {
+ #inited = false;
// Maintain a map of tor settings set by Tor Browser so that we don't
// repeatedly set the same key/values over and over.
// This map contains string keys to primitives or array values.
- _settingsCache: new Map(),
+ #settingsCache = new Map();
- _controlPort: null,
- _controlHost: null,
- _controlIPCFile: null, // An nsIFile if using IPC for control port.
- _controlPassword: null, // JS string that contains hex-encoded password.
- _SOCKSPortInfo: null, // An object that contains ipcFile, host, port.
+ #controlPort = null;
+ #controlHost = null;
+ #controlIPCFile = null; // An nsIFile if using IPC for control port.
+ #controlPassword = null; // JS string that contains hex-encoded password.
+ #SOCKSPortInfo = null; // An object that contains ipcFile, host, port.
- _controlConnection: null, // This is cached and reused.
- _connectionQueue: [],
+ #controlConnection = null; // This is cached and reused.
+ #connectionQueue = [];
// Public methods
async init() {
- if (this._inited) {
+ if (this.#inited) {
return;
}
- this._inited = true;
+ this.#inited = true;
+
+ Services.obs.addObserver(this, TorProviderTopics.ProcessExited);
+ Services.obs.addObserver(this, TorProviderTopics.ProcessRestarted);
- Services.obs.addObserver(this, TorTopics.ProcessExited);
- Services.obs.addObserver(this, TorTopics.ProcessRestarted);
+ await this.#setSockets();
- await this._setSockets();
+ this._monitorInit();
- logger.debug("TorProtocolService initialized");
- },
+ logger.debug("TorProvider initialized");
+ }
uninit() {
- Services.obs.removeObserver(this, TorTopics.ProcessExited);
- Services.obs.removeObserver(this, TorTopics.ProcessRestarted);
- this._closeConnection();
- },
+ Services.obs.removeObserver(this, TorProviderTopics.ProcessExited);
+ Services.obs.removeObserver(this, TorProviderTopics.ProcessRestarted);
+ this.#closeConnection();
+ this._monitorUninit();
+ }
observe(subject, topic, data) {
- if (topic === TorTopics.ProcessExited) {
- this._closeConnection();
- } else if (topic === TorTopics.ProcessRestarted) {
- this._reconnect();
+ if (topic === TorProviderTopics.ProcessExited) {
+ this.#closeConnection();
+ } else if (topic === TorProviderTopics.ProcessRestarted) {
+ this.#reconnect();
}
- },
+ }
// takes a Map containing tor settings
// throws on error
@@ -109,14 +126,14 @@ export const TorProtocolService = {
// only write settings that have changed
const newSettings = Array.from(aSettingsObj).filter(([setting, value]) => {
// make sure we have valid data here
- this._assertValidSetting(setting, value);
+ this.#assertValidSetting(setting, value);
- if (!this._settingsCache.has(setting)) {
+ if (!this.#settingsCache.has(setting)) {
// no cached setting, so write
return true;
}
- const cachedValue = this._settingsCache.get(setting);
+ const cachedValue = this.#settingsCache.get(setting);
if (value === cachedValue) {
return false;
} else if (Array.isArray(value) && Array.isArray(cachedValue)) {
@@ -142,21 +159,21 @@ export const TorProtocolService = {
// save settings to cache after successfully writing to Tor
for (const [setting, value] of newSettings) {
- this._settingsCache.set(setting, value);
+ this.#settingsCache.set(setting, value);
}
}
- },
+ }
async readStringArraySetting(aSetting) {
- const value = await this._readSetting(aSetting);
- this._settingsCache.set(aSetting, value);
+ const value = await this.#readSetting(aSetting);
+ this.#settingsCache.set(aSetting, value);
return value;
- },
+ }
// writes current tor settings to disk
async flushSettings() {
await this.sendCommand("SAVECONF");
- },
+ }
async connect() {
const kTorConfKeyDisableNetwork = "DisableNetwork";
@@ -164,9 +181,9 @@ export const TorProtocolService = {
settings[kTorConfKeyDisableNetwork] = false;
await this.setConfWithReply(settings);
await this.sendCommand("SAVECONF");
- lazy.TorMonitorService.clearBootstrapError();
- lazy.TorMonitorService.retrieveBootstrapStatus();
- },
+ this.clearBootstrapError();
+ this.retrieveBootstrapStatus();
+ }
async stopBootstrap() {
// Tell tor to disable use of the network; this should stop the bootstrap
@@ -180,12 +197,12 @@ export const TorProtocolService = {
// We are not interested in waiting for this, nor in **catching its error**,
// so we do not await this. We just want to be notified when the bootstrap
// status is actually updated through observers.
- lazy.TorMonitorService.retrieveBootstrapStatus();
- },
+ this.retrieveBootstrapStatus();
+ }
async newnym() {
return this.sendCommand("SIGNAL NEWNYM");
- },
+ }
// Ask tor which ports it is listening to for SOCKS connections.
// At the moment this is used only in TorCheckService.
@@ -194,7 +211,7 @@ export const TorProtocolService = {
const keyword = "net/listeners/socks";
const response = await this.sendCommand(cmd, keyword);
return TorParsers.parseReply(cmd, keyword, response);
- },
+ }
async getBridges() {
// Ideally, we would not need this function, because we should be the one
@@ -203,19 +220,19 @@ export const TorProtocolService = {
// is the most reliable way of getting the configured bridges, at the
// moment. Also, we are using this for the circuit display, which should
// work also when we are not configuring the tor daemon, but just using it.
- return this._withConnection(conn => {
+ return this.#withConnection(conn => {
return conn.getConf("bridge");
});
- },
+ }
/**
* Returns tha data about a relay or a bridge.
*
* @param {string} id The fingerprint of the node to get data about
- * @returns {NodeData}
+ * @returns {Promise<NodeData>}
*/
async getNodeInfo(id) {
- return this._withConnection(async conn => {
+ return this.#withConnection(async conn => {
const node = {
fingerprint: id,
ipAddrs: [],
@@ -259,62 +276,62 @@ export const TorProtocolService = {
}
return node;
});
- },
+ }
async onionAuthAdd(hsAddress, b64PrivateKey, isPermanent) {
- return this._withConnection(conn => {
+ return this.#withConnection(conn => {
return conn.onionAuthAdd(hsAddress, b64PrivateKey, isPermanent);
});
- },
+ }
async onionAuthRemove(hsAddress) {
- return this._withConnection(conn => {
+ return this.#withConnection(conn => {
return conn.onionAuthRemove(hsAddress);
});
- },
+ }
async onionAuthViewKeys() {
- return this._withConnection(conn => {
+ return this.#withConnection(conn => {
return conn.onionAuthViewKeys();
});
- },
+ }
// TODO: transform the following 4 functions in getters.
// Returns Tor password string or null if an error occurs.
torGetPassword() {
- return this._controlPassword;
- },
+ return this.#controlPassword;
+ }
torGetControlIPCFile() {
- return this._controlIPCFile?.clone();
- },
+ return this.#controlIPCFile?.clone();
+ }
torGetControlPort() {
- return this._controlPort;
- },
+ return this.#controlPort;
+ }
torGetSOCKSPortInfo() {
- return this._SOCKSPortInfo;
- },
+ return this.#SOCKSPortInfo;
+ }
get torControlPortInfo() {
const info = {
- password: this._controlPassword,
+ password: this.#controlPassword,
};
- if (this._controlIPCFile) {
- info.ipcFile = this._controlIPCFile?.clone();
+ if (this.#controlIPCFile) {
+ info.ipcFile = this.#controlIPCFile?.clone();
}
- if (this._controlPort) {
- info.host = this._controlHost;
- info.port = this._controlPort;
+ if (this.#controlPort) {
+ info.host = this.#controlHost;
+ info.port = this.#controlPort;
}
return info;
- },
+ }
get torSOCKSPortInfo() {
- return this._SOCKSPortInfo;
- },
+ return this.#SOCKSPortInfo;
+ }
// Public, but called only internally
@@ -326,7 +343,7 @@ export const TorProtocolService = {
let timeout = 250;
let reply;
while (leftConnAttempts-- > 0) {
- const response = await this._trySend(cmd, args, leftConnAttempts == 0);
+ const response = await this.#trySend(cmd, args, leftConnAttempts === 0);
if (response.connected) {
reply = response.reply;
break;
@@ -360,7 +377,7 @@ export const TorProtocolService = {
}
return reply;
- },
+ }
// Perform a SETCONF command.
// aSettingsObj should be a JavaScript object with keys (property values)
@@ -398,39 +415,39 @@ export const TorProtocolService = {
}
await this.sendCommand("SETCONF", args.join(" "));
- },
+ }
// Public, never called?
async readBoolSetting(aSetting) {
- let value = await this._readBoolSetting(aSetting);
- this._settingsCache.set(aSetting, value);
+ let value = await this.#readBoolSetting(aSetting);
+ this.#settingsCache.set(aSetting, value);
return value;
- },
+ }
async readStringSetting(aSetting) {
- let value = await this._readStringSetting(aSetting);
- this._settingsCache.set(aSetting, value);
+ let value = await this.#readStringSetting(aSetting);
+ this.#settingsCache.set(aSetting, value);
return value;
- },
+ }
// Private
- async _setSockets() {
+ async #setSockets() {
try {
const isWindows = TorLauncherUtil.isWindows;
// Determine how Tor Launcher will connect to the Tor control port.
// Environment variables get top priority followed by preferences.
if (!isWindows && Services.env.exists("TOR_CONTROL_IPC_PATH")) {
const ipcPath = Services.env.get("TOR_CONTROL_IPC_PATH");
- this._controlIPCFile = new lazy.FileUtils.File(ipcPath);
+ this.#controlIPCFile = new lazy.FileUtils.File(ipcPath);
} else {
// Check for TCP host and port environment variables.
if (Services.env.exists("TOR_CONTROL_HOST")) {
- this._controlHost = Services.env.get("TOR_CONTROL_HOST");
+ this.#controlHost = Services.env.get("TOR_CONTROL_HOST");
}
if (Services.env.exists("TOR_CONTROL_PORT")) {
- this._controlPort = parseInt(
+ this.#controlPort = parseInt(
Services.env.get("TOR_CONTROL_PORT"),
10
);
@@ -442,20 +459,20 @@ export const TorProtocolService = {
"extensions.torlauncher.control_port_use_ipc",
false
);
- if (!this._controlHost && !this._controlPort && useIPC) {
- this._controlIPCFile = TorLauncherUtil.getTorFile(
+ if (!this.#controlHost && !this.#controlPort && useIPC) {
+ this.#controlIPCFile = TorLauncherUtil.getTorFile(
"control_ipc",
false
);
} else {
- if (!this._controlHost) {
- this._controlHost = Services.prefs.getCharPref(
+ if (!this.#controlHost) {
+ this.#controlHost = Services.prefs.getCharPref(
"extensions.torlauncher.control_host",
"127.0.0.1"
);
}
- if (!this._controlPort) {
- this._controlPort = Services.prefs.getIntPref(
+ if (!this.#controlPort) {
+ this.#controlPort = Services.prefs.getIntPref(
"extensions.torlauncher.control_port",
9151
);
@@ -465,46 +482,46 @@ export const TorProtocolService = {
// Populate _controlPassword so it is available when starting tor.
if (Services.env.exists("TOR_CONTROL_PASSWD")) {
- this._controlPassword = Services.env.get("TOR_CONTROL_PASSWD");
+ this.#controlPassword = Services.env.get("TOR_CONTROL_PASSWD");
} else if (Services.env.exists("TOR_CONTROL_COOKIE_AUTH_FILE")) {
// TODO: test this code path (TOR_CONTROL_COOKIE_AUTH_FILE).
const cookiePath = Services.env.get("TOR_CONTROL_COOKIE_AUTH_FILE");
if (cookiePath) {
- this._controlPassword = await this._readAuthenticationCookie(
+ this.#controlPassword = await this.#readAuthenticationCookie(
cookiePath
);
}
}
- if (!this._controlPassword) {
- this._controlPassword = this._generateRandomPassword();
+ if (!this.#controlPassword) {
+ this.#controlPassword = this.#generateRandomPassword();
}
- this._SOCKSPortInfo = TorLauncherUtil.getPreferredSocksConfiguration();
- TorLauncherUtil.setProxyConfiguration(this._SOCKSPortInfo);
+ this.#SOCKSPortInfo = TorLauncherUtil.getPreferredSocksConfiguration();
+ TorLauncherUtil.setProxyConfiguration(this.#SOCKSPortInfo);
// Set the global control port info parameters.
lazy.configureControlPortModule(
- this._controlIPCFile,
- this._controlHost,
- this._controlPort,
- this._controlPassword
+ this.#controlIPCFile,
+ this.#controlHost,
+ this.#controlPort,
+ this.#controlPassword
);
} catch (e) {
logger.error("Failed to get environment variables", e);
}
- },
+ }
- _assertValidSettingKey(aSetting) {
+ #assertValidSettingKey(aSetting) {
// ensure the 'key' is a string
if (typeof aSetting !== "string") {
throw new Error(
`Expected setting of type string but received ${typeof aSetting}`
);
}
- },
+ }
- _assertValidSetting(aSetting, aValue) {
- this._assertValidSettingKey(aSetting);
+ #assertValidSetting(aSetting, aValue) {
+ this.#assertValidSettingKey(aSetting);
switch (typeof aValue) {
case "boolean":
case "string":
@@ -528,29 +545,29 @@ export const TorProtocolService = {
`Invalid object type received for setting '${aSetting}'`
);
}
- },
+ }
// Perform a GETCONF command.
- async _readSetting(aSetting) {
- this._assertValidSettingKey(aSetting);
+ async #readSetting(aSetting) {
+ this.#assertValidSettingKey(aSetting);
const cmd = "GETCONF";
let reply = await this.sendCommand(cmd, aSetting);
return TorParsers.parseReply(cmd, aSetting, reply);
- },
+ }
- async _readStringSetting(aSetting) {
- let lineArray = await this._readSetting(aSetting);
+ async #readStringSetting(aSetting) {
+ let lineArray = await this.#readSetting(aSetting);
if (lineArray.length !== 1) {
throw new Error(
`Expected an array with length 1 but received array of length ${lineArray.length}`
);
}
return lineArray[0];
- },
+ }
- async _readBoolSetting(aSetting) {
- const value = this._readStringSetting(aSetting);
+ async #readBoolSetting(aSetting) {
+ const value = this.#readStringSetting(aSetting);
switch (value) {
case "0":
return false;
@@ -559,16 +576,16 @@ export const TorProtocolService = {
default:
throw new Error(`Expected boolean (1 or 0) but received '${value}'`);
}
- },
+ }
- async _trySend(cmd, args, rethrow) {
+ async #trySend(cmd, args, rethrow) {
let connected = false;
let reply;
let leftAttempts = 2;
while (leftAttempts-- > 0) {
let conn;
try {
- conn = await this._getConnection();
+ conn = await this.#getConnection();
} catch (e) {
logger.error("Cannot get a connection to the control port", e);
if (leftAttempts == 0 && rethrow) {
@@ -584,105 +601,105 @@ export const TorProtocolService = {
reply = await conn.sendCommand(cmd + (args ? " " + args : ""));
if (reply) {
// Return for reuse.
- this._returnConnection();
+ this.#returnConnection();
} else {
// Connection is bad.
logger.warn(
"sendCommand returned an empty response, taking the connection as broken and closing it."
);
- this._closeConnection();
+ this.#closeConnection();
}
} catch (e) {
logger.error(`Cannot send the command ${cmd}`, e);
- this._closeConnection();
+ this.#closeConnection();
if (leftAttempts == 0 && rethrow) {
throw e;
}
}
}
return { connected, reply };
- },
+ }
- // Opens an authenticated connection, sets it to this._controlConnection, and
+ // Opens an authenticated connection, sets it to this.#controlConnection, and
// return it.
- async _getConnection() {
- if (!this._controlConnection) {
- this._controlConnection = await lazy.controller();
+ async #getConnection() {
+ if (!this.#controlConnection) {
+ this.#controlConnection = await lazy.controller();
}
- if (this._controlConnection.inUse) {
+ if (this.#controlConnection.inUse) {
await new Promise((resolve, reject) =>
- this._connectionQueue.push({ resolve, reject })
+ this.#connectionQueue.push({ resolve, reject })
);
} else {
- this._controlConnection.inUse = true;
+ this.#controlConnection.inUse = true;
}
- return this._controlConnection;
- },
+ return this.#controlConnection;
+ }
- _returnConnection() {
- if (this._connectionQueue.length) {
- this._connectionQueue.shift().resolve();
+ #returnConnection() {
+ if (this.#connectionQueue.length) {
+ this.#connectionQueue.shift().resolve();
} else {
- this._controlConnection.inUse = false;
+ this.#controlConnection.inUse = false;
}
- },
+ }
- async _withConnection(func) {
+ async #withConnection(func) {
// TODO: Make more robust?
- const conn = await this._getConnection();
+ const conn = await this.#getConnection();
try {
return await func(conn);
} finally {
- this._returnConnection();
+ this.#returnConnection();
}
- },
+ }
// If aConn is omitted, the cached connection is closed.
- _closeConnection() {
- if (this._controlConnection) {
+ #closeConnection() {
+ if (this.#controlConnection) {
logger.info("Closing the control connection");
- this._controlConnection.close();
- this._controlConnection = null;
+ this.#controlConnection.close();
+ this.#controlConnection = null;
}
- for (const promise of this._connectionQueue) {
+ for (const promise of this.#connectionQueue) {
promise.reject("Connection closed");
}
- this._connectionQueue = [];
- },
+ this.#connectionQueue = [];
+ }
- async _reconnect() {
- this._closeConnection();
- const conn = await this._getConnection();
+ async #reconnect() {
+ this.#closeConnection();
+ const conn = await this.#getConnection();
logger.debug("Reconnected to the control port.");
- this._returnConnection(conn);
- },
+ this.#returnConnection(conn);
+ }
- async _readAuthenticationCookie(aPath) {
+ async #readAuthenticationCookie(aPath) {
const bytes = await IOUtils.read(aPath);
- return Array.from(bytes, b => this._toHex(b, 2)).join("");
- },
+ return Array.from(bytes, b => this.#toHex(b, 2)).join("");
+ }
// Returns a random 16 character password, hex-encoded.
- _generateRandomPassword() {
+ #generateRandomPassword() {
// Similar to Vidalia's crypto_rand_string().
const kPasswordLen = 16;
const kMinCharCode = "!".charCodeAt(0);
const kMaxCharCode = "~".charCodeAt(0);
let pwd = "";
for (let i = 0; i < kPasswordLen; ++i) {
- const val = this._cryptoRandInt(kMaxCharCode - kMinCharCode + 1);
+ const val = this.#cryptoRandInt(kMaxCharCode - kMinCharCode + 1);
if (val < 0) {
logger.error("_cryptoRandInt() failed");
return null;
}
- pwd += this._toHex(kMinCharCode + val, 2);
+ pwd += this.#toHex(kMinCharCode + val, 2);
}
return pwd;
- },
+ }
// Returns -1 upon failure.
- _cryptoRandInt(aMax) {
+ #cryptoRandInt(aMax) {
// Based on tor's crypto_rand_int().
const maxUInt = 0xffffffff;
if (aMax <= 0 || aMax > maxUInt) {
@@ -697,9 +714,588 @@ export const TorProtocolService = {
val = uint32[0];
}
return val % aMax;
- },
+ }
- _toHex(aValue, aMinLen) {
+ #toHex(aValue, aMinLen) {
return aValue.toString(16).padStart(aMinLen, "0");
- },
-};
+ }
+
+ // Former TorMonitorService implementation.
+ // FIXME: Refactor and integrate more with the rest of the class.
+
+ _connection = null;
+ _eventHandlers = {};
+ _torLog = []; // Array of objects with date, type, and msg properties
+ _startTimeout = null;
+
+ _isBootstrapDone = false;
+ _lastWarningPhase = null;
+ _lastWarningReason = null;
+
+ _torProcess = null;
+
+ _inited = false;
+
+ /**
+ * Stores the nodes of a circuit. Keys are cicuit IDs, and values are the node
+ * fingerprints.
+ *
+ * Theoretically, we could hook this map up to the new identity notification,
+ * but in practice it does not work. Tor pre-builds circuits, and the NEWNYM
+ * signal does not affect them. So, we might end up using a circuit that was
+ * 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[]>}
+ */
+ _circuits = new Map();
+ /**
+ * The last used bridge, or null if bridges are not in use or if it was not
+ * possible to detect the bridge. This needs the user to have specified bridge
+ * lines with fingerprints to work.
+ *
+ * @type {NodeFingerprint?}
+ */
+ _currentBridge = null;
+
+ // Public methods
+
+ // Starts Tor, if needed, and starts monitoring for events
+ _monitorInit() {
+ if (this._inited) {
+ return;
+ }
+ this._inited = true;
+
+ // We always liten to these events, because they are needed for the circuit
+ // display.
+ this._eventHandlers = new Map([
+ ["CIRC", this._processCircEvent.bind(this)],
+ ["STREAM", this._processStreamEvent.bind(this)],
+ ]);
+
+ if (this.ownsTorDaemon) {
+ // When we own the tor daemon, we listen to more events, that are used
+ // for about:torconnect or for showing the logs in the settings page.
+ this._eventHandlers.set("STATUS_CLIENT", (_eventType, lines) =>
+ this._processBootstrapStatus(lines[0], false)
+ );
+ this._eventHandlers.set("NOTICE", this._processLog.bind(this));
+ this._eventHandlers.set("WARN", this._processLog.bind(this));
+ this._eventHandlers.set("ERR", this._processLog.bind(this));
+ this._controlTor();
+ } else {
+ this._startEventMonitor();
+ }
+ logger.info("TorMonitorService initialized");
+ }
+
+ // Closes the connection that monitors for events.
+ // When Tor is started by Tor Browser, it is configured to exit when the
+ // control connection is closed. Therefore, as a matter of facts, calling this
+ // function also makes the child Tor instance stop.
+ _monitorUninit() {
+ if (this._torProcess) {
+ this._torProcess.forget();
+ this._torProcess.onExit = null;
+ this._torProcess.onRestart = null;
+ this._torProcess = null;
+ }
+ this._shutDownEventMonitor();
+ }
+
+ async retrieveBootstrapStatus() {
+ if (!this._connection) {
+ throw new Error("Event monitor connection not available");
+ }
+
+ // TODO: Unify with TorProtocolService.sendCommand and put everything in the
+ // reviewed torbutton replacement.
+ const cmd = "GETINFO";
+ const key = "status/bootstrap-phase";
+ let reply = await this._connection.sendCommand(`${cmd} ${key}`);
+
+ // A typical reply looks like:
+ // 250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"
+ // 250 OK
+ reply = TorParsers.parseCommandResponse(reply);
+ if (!TorParsers.commandSucceeded(reply)) {
+ throw new Error(`${cmd} failed`);
+ }
+ reply = TorParsers.parseReply(cmd, key, reply);
+ if (reply.length) {
+ this._processBootstrapStatus(reply[0], true);
+ }
+ }
+
+ // Returns captured log message as a text string (one message per line).
+ getLog() {
+ return this._torLog
+ .map(logObj => {
+ const timeStr = logObj.date
+ .toISOString()
+ .replace("T", " ")
+ .replace("Z", "");
+ return `${timeStr} [${logObj.type}] ${logObj.msg}`;
+ })
+ .join(TorLauncherUtil.isWindows ? "\r\n" : "\n");
+ }
+
+ // true if we launched and control tor, false if using system tor
+ get ownsTorDaemon() {
+ return TorLauncherUtil.shouldStartAndOwnTor;
+ }
+
+ get isBootstrapDone() {
+ return this._isBootstrapDone;
+ }
+
+ clearBootstrapError() {
+ this._lastWarningPhase = null;
+ this._lastWarningReason = null;
+ }
+
+ get isRunning() {
+ return !!this._connection;
+ }
+
+ /**
+ * Return the data about the current bridge, if any, or null.
+ * We can detect bridge only when the configured bridge lines include the
+ * fingerprints.
+ *
+ * @returns {NodeData?} The node information, or null if the first node
+ * is not a bridge, or no circuit has been opened, yet.
+ */
+ get currentBridge() {
+ return this._currentBridge;
+ }
+
+ // Private methods
+
+ async _startProcess() {
+ // TorProcess should be instanced once, then always reused and restarted
+ // only through the prompt it exposes when the controlled process dies.
+ if (!this._torProcess) {
+ this._torProcess = new lazy.TorProcess(
+ this.torControlPortInfo,
+ this.torSOCKSPortInfo
+ );
+ this._torProcess.onExit = () => {
+ this._shutDownEventMonitor();
+ Services.obs.notifyObservers(null, TorProviderTopics.ProcessExited);
+ };
+ this._torProcess.onRestart = async () => {
+ this._shutDownEventMonitor();
+ await this._controlTor();
+ Services.obs.notifyObservers(null, TorProviderTopics.ProcessRestarted);
+ };
+ }
+
+ // Already running, but we did not start it
+ if (this._torProcess.isRunning) {
+ return false;
+ }
+
+ try {
+ await this._torProcess.start();
+ if (this._torProcess.isRunning) {
+ logger.info("tor started");
+ this._torProcessStartTime = Date.now();
+ }
+ } catch (e) {
+ // TorProcess already logs the error.
+ this._lastWarningPhase = "startup";
+ this._lastWarningReason = e.toString();
+ }
+ return this._torProcess.isRunning;
+ }
+
+ async _controlTor() {
+ if (!this._torProcess?.isRunning && !(await this._startProcess())) {
+ logger.error("Tor not running, not starting to monitor it.");
+ return;
+ }
+
+ let delayMS = ControlConnTimings.initialDelayMS;
+ const callback = async () => {
+ if (await this._startEventMonitor()) {
+ this.retrieveBootstrapStatus().catch(e => {
+ logger.warn("Could not get the initial bootstrap status", e);
+ });
+
+ // FIXME: TorProcess is misleading here. We should use a topic related
+ // to having a control port connection, instead.
+ logger.info(`Notifying ${TorProviderTopics.ProcessIsReady}`);
+ Services.obs.notifyObservers(null, TorProviderTopics.ProcessIsReady);
+
+ // We reset this here hoping that _shutDownEventMonitor can interrupt
+ // the current monitor, either by calling clearTimeout and preventing it
+ // from starting, or by closing the control port connection.
+ if (this._startTimeout === null) {
+ logger.warn("Someone else reset _startTimeout!");
+ }
+ this._startTimeout = null;
+ } else if (
+ Date.now() - this._torProcessStartTime >
+ ControlConnTimings.timeoutMS
+ ) {
+ let s = TorLauncherUtil.getLocalizedString("tor_controlconn_failed");
+ this._lastWarningPhase = "startup";
+ this._lastWarningReason = s;
+ logger.info(s);
+ if (this._startTimeout === null) {
+ logger.warn("Someone else reset _startTimeout!");
+ }
+ this._startTimeout = null;
+ } else {
+ delayMS *= 2;
+ if (delayMS > ControlConnTimings.maxRetryMS) {
+ delayMS = ControlConnTimings.maxRetryMS;
+ }
+ this._startTimeout = setTimeout(() => {
+ logger.debug(`Control port not ready, waiting ${delayMS / 1000}s.`);
+ callback();
+ }, delayMS);
+ }
+ };
+ // Check again, in the unfortunate case in which the execution was alrady
+ // queued, but was waiting network code.
+ if (this._startTimeout === null) {
+ this._startTimeout = setTimeout(callback, delayMS);
+ } else {
+ logger.error("Possible race? Refusing to start the timeout again");
+ }
+ }
+
+ async _startEventMonitor() {
+ if (this._connection) {
+ return true;
+ }
+
+ let conn;
+ try {
+ conn = await lazy.controller();
+ } catch (e) {
+ logger.error("Cannot open a control port connection", e);
+ if (conn) {
+ try {
+ conn.close();
+ } catch (e) {
+ logger.error(
+ "Also, the connection is not null but cannot be closed",
+ e
+ );
+ }
+ }
+ return false;
+ }
+
+ // TODO: optionally monitor INFO and DEBUG log messages.
+ try {
+ await conn.setEvents(Array.from(this._eventHandlers.keys()));
+ } catch (e) {
+ logger.error("SETEVENTS failed", e);
+ conn.close();
+ return false;
+ }
+
+ if (this._torProcess) {
+ this._torProcess.connectionWorked();
+ }
+ if (this.ownsTorDaemon && !TorLauncherUtil.shouldOnlyConfigureTor) {
+ try {
+ await this._takeTorOwnership(conn);
+ } catch (e) {
+ logger.warn("Could not take ownership of the Tor daemon", e);
+ }
+ }
+
+ this._connection = conn;
+
+ for (const [type, callback] of this._eventHandlers.entries()) {
+ this._monitorEvent(type, callback);
+ }
+
+ // Populate the circuit map already, in case we are connecting to an
+ // external tor daemon.
+ try {
+ const reply = await this._connection.sendCommand(
+ "GETINFO circuit-status"
+ );
+ const lines = reply.split(/\r?\n/);
+ if (lines.shift() === "250+circuit-status=") {
+ for (const line of lines) {
+ if (line === ".") {
+ break;
+ }
+ // _processCircEvent processes only one line at a time
+ this._processCircEvent("CIRC", [line]);
+ }
+ }
+ } catch (e) {
+ logger.warn("Could not populate the initial circuit map", e);
+ }
+
+ return true;
+ }
+
+ // Try to become the primary controller (TAKEOWNERSHIP).
+ async _takeTorOwnership(conn) {
+ try {
+ conn.takeOwnership();
+ } catch (e) {
+ logger.warn("Take ownership failed", e);
+ return;
+ }
+ try {
+ conn.resetOwningControllerProcess();
+ } catch (e) {
+ logger.warn("Clear owning controller process failed", e);
+ }
+ }
+
+ _monitorEvent(type, callback) {
+ logger.info(`Watching events of type ${type}.`);
+ let replyObj = {};
+ this._connection.watchEvent(
+ type,
+ null,
+ line => {
+ if (!line) {
+ return;
+ }
+ logger.debug("Event response: ", line);
+ const isComplete = TorParsers.parseReplyLine(line, replyObj);
+ if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
+ return;
+ }
+ const reply = replyObj;
+ replyObj = {};
+ if (reply.statusCode !== TorStatuses.EventNotification) {
+ logger.error("Unexpected event status code:", reply.statusCode);
+ return;
+ }
+ if (!reply.lineArray[0].startsWith(`${type} `)) {
+ logger.error("Wrong format for the first line:", reply.lineArray[0]);
+ return;
+ }
+ reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
+ try {
+ callback(type, reply.lineArray);
+ } catch (e) {
+ logger.error("Exception while handling an event", reply, e);
+ }
+ },
+ true
+ );
+ }
+
+ _processLog(type, lines) {
+ if (type === "WARN" || type === "ERR") {
+ // Notify so that Copy Log can be enabled.
+ Services.obs.notifyObservers(null, TorProviderTopics.HasWarnOrErr);
+ }
+
+ const date = new Date();
+ const maxEntries = Services.prefs.getIntPref(
+ "extensions.torlauncher.max_tor_log_entries",
+ 1000
+ );
+ if (maxEntries > 0 && this._torLog.length >= maxEntries) {
+ this._torLog.splice(0, 1);
+ }
+
+ const msg = lines.join("\n");
+ this._torLog.push({ date, type, msg });
+ const logString = `Tor ${type}: ${msg}`;
+ logger.info(logString);
+ }
+
+ // Process a bootstrap status to update the current state, and broadcast it
+ // to TorBootstrapStatus observers.
+ // If aSuppressErrors is true, errors are ignored. This is used when we
+ // are handling the response to a "GETINFO status/bootstrap-phase" command.
+ _processBootstrapStatus(aStatusMsg, aSuppressErrors) {
+ const statusObj = TorParsers.parseBootstrapStatus(aStatusMsg);
+ if (!statusObj) {
+ return;
+ }
+
+ // Notify observers
+ statusObj.wrappedJSObject = statusObj;
+ Services.obs.notifyObservers(statusObj, "TorBootstrapStatus");
+
+ if (statusObj.PROGRESS === 100) {
+ this._isBootstrapDone = true;
+ try {
+ Services.prefs.setBoolPref(Preferences.PromptAtStartup, false);
+ } catch (e) {
+ logger.warn(`Cannot set ${Preferences.PromptAtStartup}`, e);
+ }
+ return;
+ }
+
+ this._isBootstrapDone = false;
+
+ if (
+ statusObj.TYPE === "WARN" &&
+ statusObj.RECOMMENDATION !== "ignore" &&
+ !aSuppressErrors
+ ) {
+ this._notifyBootstrapError(statusObj);
+ }
+ }
+
+ _notifyBootstrapError(statusObj) {
+ try {
+ Services.prefs.setBoolPref(Preferences.PromptAtStartup, true);
+ } catch (e) {
+ logger.warn(`Cannot set ${Preferences.PromptAtStartup}`, e);
+ }
+ const phase = TorLauncherUtil.getLocalizedBootstrapStatus(statusObj, "TAG");
+ const reason = TorLauncherUtil.getLocalizedBootstrapStatus(
+ statusObj,
+ "REASON"
+ );
+ const details = TorLauncherUtil.getFormattedLocalizedString(
+ "tor_bootstrap_failed_details",
+ [phase, reason],
+ 2
+ );
+ logger.error(
+ `Tor bootstrap error: [${statusObj.TAG}/${statusObj.REASON}] ${details}`
+ );
+
+ if (
+ statusObj.TAG !== this._lastWarningPhase ||
+ statusObj.REASON !== this._lastWarningReason
+ ) {
+ this._lastWarningPhase = statusObj.TAG;
+ this._lastWarningReason = statusObj.REASON;
+
+ const message = TorLauncherUtil.getLocalizedString(
+ "tor_bootstrap_failed"
+ );
+ Services.obs.notifyObservers(
+ { message, details },
+ TorProviderTopics.BootstrapError
+ );
+ }
+ }
+
+ async _processCircEvent(_type, lines) {
+ const builtEvent =
+ /^(?<CircuitID>[a-zA-Z0-9]{1,16})\sBUILT\s(?<Path>(?:,?\$[0-9a-fA-F]{40}(?:~[a-zA-Z0-9]{1,19})?)+)/.exec(
+ lines[0]
+ );
+ const closedEvent = /^(?<ID>[a-zA-Z0-9]{1,16})\sCLOSED/.exec(lines[0]);
+ if (builtEvent) {
+ const fp = /\$([0-9a-fA-F]{40})/g;
+ const nodes = Array.from(builtEvent.groups.Path.matchAll(fp), g =>
+ g[1].toUpperCase()
+ );
+ this._circuits.set(builtEvent.groups.CircuitID, nodes);
+ // Ignore circuits of length 1, that are used, for example, to probe
+ // bridges. So, only store them, since we might see streams that use them,
+ // but then early-return.
+ if (nodes.length === 1) {
+ return;
+ }
+ // 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.
+ // this._checkCredentials(lines[0], nodes);
+ if (this._currentBridge?.fingerprint !== nodes[0]) {
+ const nodeInfo = await this.getNodeInfo(nodes[0]);
+ let notify = false;
+ if (nodeInfo?.bridgeType) {
+ logger.info(`Bridge changed to ${nodes[0]}`);
+ this._currentBridge = nodeInfo;
+ notify = true;
+ } else if (this._currentBridge) {
+ logger.info("Bridges disabled");
+ this._currentBridge = null;
+ notify = true;
+ }
+ if (notify) {
+ Services.obs.notifyObservers(
+ null,
+ TorProviderTopics.BridgeChanged,
+ this._currentBridge
+ );
+ }
+ }
+ } else if (closedEvent) {
+ this._circuits.delete(closedEvent.groups.ID);
+ }
+ }
+
+ _processStreamEvent(_type, lines) {
+ // The first block is the stream ID, which we do not need at the moment.
+ const succeeedEvent =
+ /^[a-zA-Z0-9]{1,16}\sSUCCEEDED\s(?<CircuitID>[a-zA-Z0-9]{1,16})/.exec(
+ lines[0]
+ );
+ if (!succeeedEvent) {
+ return;
+ }
+ const circuit = this._circuits.get(succeeedEvent.groups.CircuitID);
+ if (!circuit) {
+ logger.error(
+ "Seen a STREAM SUCCEEDED with an unknown circuit. Not notifying observers.",
+ lines[0]
+ );
+ return;
+ }
+ this._checkCredentials(lines[0], circuit);
+ }
+
+ /**
+ * Check if a STREAM or CIRC response line contains SOCKS_USERNAME and
+ * SOCKS_PASSWORD. In case, notify observers that we could associate a certain
+ * circuit to these credentials.
+ *
+ * @param {string} line The circ or stream line to check
+ * @param {NodeFingerprint[]} circuit The fingerprints of the nodes in the
+ * circuit.
+ */
+ _checkCredentials(line, circuit) {
+ const username = /SOCKS_USERNAME=("(?:[^"\\]|\\.)*")/.exec(line);
+ const password = /SOCKS_PASSWORD=("(?:[^"\\]|\\.)*")/.exec(line);
+ if (!username || !password) {
+ return;
+ }
+ Services.obs.notifyObservers(
+ {
+ wrappedJSObject: {
+ username: TorParsers.unescapeString(username[1]),
+ password: TorParsers.unescapeString(password[1]),
+ circuit,
+ },
+ },
+ TorProviderTopics.StreamSucceeded
+ );
+ }
+
+ _shutDownEventMonitor() {
+ try {
+ this._connection?.close();
+ } catch (e) {
+ logger.error("Could not close the connection to the control port", e);
+ }
+ this._connection = null;
+ if (this._startTimeout !== null) {
+ clearTimeout(this._startTimeout);
+ this._startTimeout = null;
+ }
+ this._isBootstrapDone = false;
+ this.clearBootstrapError();
+ }
+}
+
+// TODO: Stop defining TorProtocolService, make the builder instance the
+// TorProvider.
+export const TorProtocolService = new TorProvider();
=====================================
toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
=====================================
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
+});
+
+export const TorProviderTopics = Object.freeze({
+ ProcessIsReady: "TorProcessIsReady",
+ ProcessExited: "TorProcessExited",
+ ProcessRestarted: "TorProcessRestarted",
+ BootstrapStatus: "TorBootstrapStatus",
+ BootstrapError: "TorBootstrapError",
+ HasWarnOrErr: "TorLogHasWarnOrErr",
+ BridgeChanged: "TorBridgeChanged",
+ StreamSucceeded: "TorStreamSucceeded",
+});
+
+export class TorProviderBuilder {
+ static async init() {
+ await lazy.TorProtocolService.init();
+ }
+
+ static uninit() {
+ lazy.TorProtocolService.uninit();
+ }
+
+ // TODO: Switch to an async build?
+ static build() {
+ return lazy.TorProtocolService;
+ }
+}
=====================================
toolkit/components/tor-launcher/TorStartupService.sys.mjs
=====================================
@@ -5,8 +5,7 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
- TorMonitorService: "resource://gre/modules/TorMonitorService.sys.mjs",
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
});
ChromeUtils.defineModuleGetter(
@@ -33,24 +32,18 @@ let gInited = false;
// When it observes profile-after-change, it initializes whatever is needed to
// launch Tor.
export class TorStartupService {
- _defaultPreferencesAreLoaded = false;
-
observe(aSubject, aTopic, aData) {
if (aTopic === BrowserTopics.ProfileAfterChange && !gInited) {
- this._init();
+ this.#init();
} else if (aTopic === BrowserTopics.QuitApplicationGranted) {
- this._uninit();
+ this.#uninit();
}
}
- async _init() {
+ async #init() {
Services.obs.addObserver(this, BrowserTopics.QuitApplicationGranted);
- // Starts TorProtocolService first, because it configures the controller
- // factory, too.
- await lazy.TorProtocolService.init();
- lazy.TorMonitorService.init();
-
+ await lazy.TorProviderBuilder.init();
lazy.TorSettings.init();
lazy.TorConnect.init();
@@ -59,17 +52,11 @@ export class TorStartupService {
gInited = true;
}
- _uninit() {
+ #uninit() {
Services.obs.removeObserver(this, BrowserTopics.QuitApplicationGranted);
lazy.TorDomainIsolator.uninit();
-
- // Close any helper connection first...
- lazy.TorProtocolService.uninit();
- // ... and only then closes the event monitor connection, which will cause
- // Tor to stop.
- lazy.TorMonitorService.uninit();
-
+ lazy.TorProviderBuilder.uninit();
lazy.TorLauncherUtil.cleanupTempDirectories();
}
}
=====================================
toolkit/components/tor-launcher/moz.build
=====================================
@@ -7,6 +7,7 @@ EXTRA_JS_MODULES += [
"TorParsers.sys.mjs",
"TorProcess.sys.mjs",
"TorProtocolService.sys.mjs",
+ "TorProviderBuilder.sys.mjs",
"TorStartupService.sys.mjs",
]
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/9722ca…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/9722ca…
You're receiving this email because of your account on gitlab.torproject.org.
1
0