[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-115.5.0esr-13.5-1] 8 commits: fixup! Bug 25741: TBA: Disable GeckoNetworkManager



Title: GitLab

Pier Angelo Vendrame pushed to branch tor-browser-115.5.0esr-13.5-1 at The Tor Project / Applications / Tor Browser

Commits:

  • 5ad20208
    by Pier Angelo Vendrame at 2023-12-06T18:31:01+01:00
    fixup! Bug 25741: TBA: Disable GeckoNetworkManager
    
    Fixed a trailing whitespace
    
  • 8c95910c
    by Pier Angelo Vendrame at 2023-12-06T18:31:02+01:00
    fixup! Bug 40597: Implement TorSettings module
    
    Implement an Android-specific transport, that relies on the Java side
    to start the domain fronting proxy.
    
  • fb609914
    by Pier Angelo Vendrame at 2023-12-07T19:35:29+01:00
    Bug 42247: Android helpers for the TorProvider
    
    GeckoView is missing some API we use on desktop for the integration
    with the tor daemon, such as subprocess.
    Therefore, we need to implement them in Java and plumb the data
    back and forth between JS and Java.
    
  • 1b7630ec
    by Pier Angelo Vendrame at 2023-12-07T19:35:34+01:00
    fixup! Bug 40933: Add tor-launcher functionality
    
    Use a custom TorProcessAndroid in the TorProvider on Android.
    
  • d0ae1f7e
    by Pier Angelo Vendrame at 2023-12-07T19:35:35+01:00
    fixup! Bug 42247: Android helpers for the TorProvider
    
    Test to plumb down the circuit display data
    
  • 51d44491
    by Pier Angelo Vendrame at 2023-12-07T19:35:35+01:00
    fixup! Bug 42247: Android helpers for the TorProvider
    
    Switch from a gate based on the update channel to a gate based on a
    pref.
    Also, draft the JS part for TorSettings events.
    
  • be3afbb8
    by Pier Angelo Vendrame at 2023-12-07T19:35:55+01:00
    fixup! Bug 42247: Android helpers for the TorProvider
    
    Keep only a TorProcess in memory at a time.
    When we forget a process from JS, we also delete the Java instance.
    When we start a new process, we delete the old one if any (but emit a
    warning in logs, in case).
    
  • 766abfe6
    by Pier Angelo Vendrame at 2023-12-07T19:35:58+01:00
    fixup! Bug 40933: Add tor-launcher functionality
    
    Make TorProcessAndroid.forget signal to Java the forget request.
    

13 changed files:

Changes:

  • mobile/android/components/geckoview/GeckoViewStartup.jsm
    ... ... @@ -5,6 +5,10 @@
    5 5
     
    
    6 6
     var EXPORTED_SYMBOLS = ["GeckoViewStartup"];
    
    7 7
     
    
    8
    +const { AppConstants } = ChromeUtils.importESModule(
    
    9
    +  "resource://gre/modules/AppConstants.sys.mjs"
    
    10
    +);
    
    11
    +
    
    8 12
     const { GeckoViewUtils } = ChromeUtils.importESModule(
    
    9 13
       "resource://gre/modules/GeckoViewUtils.sys.mjs"
    
    10 14
     );
    
    ... ... @@ -17,6 +21,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
    17 21
       PdfJs: "resource://pdf.js/PdfJs.sys.mjs",
    
    18 22
       Preferences: "resource://gre/modules/Preferences.sys.mjs",
    
    19 23
       RFPHelper: "resource://gre/modules/RFPHelper.sys.mjs",
    
    24
    +  TorAndroidIntegration: "resource://gre/modules/TorAndroidIntegration.sys.mjs",
    
    20 25
       TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
    
    21 26
     });
    
    22 27
     
    
    ... ... @@ -259,6 +264,7 @@ class GeckoViewStartup {
    259 264
               "GeckoView:SetLocale",
    
    260 265
             ]);
    
    261 266
     
    
    267
    +        lazy.TorAndroidIntegration.init();
    
    262 268
             lazy.TorDomainIsolator.init();
    
    263 269
     
    
    264 270
             Services.obs.addObserver(this, "browser-idle-startup-tasks-finished");
    

  • mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
    ... ... @@ -167,7 +167,7 @@ public final class GeckoRuntime implements Parcelable {
    167 167
           if (!BuildConfig.TOR_BROWSER) {
    
    168 168
             GeckoNetworkManager.getInstance().start(GeckoAppShell.getApplicationContext());
    
    169 169
           } else {
    
    170
    -        Log.d(LOGTAG, "Tor Browser: skip GeckoNetworkManager startup"); 
    
    170
    +        Log.d(LOGTAG, "Tor Browser: skip GeckoNetworkManager startup");
    
    171 171
           }
    
    172 172
     
    
    173 173
           // Set settings that may have changed between last app opening
    
    ... ... @@ -230,6 +230,8 @@ public final class GeckoRuntime implements Parcelable {
    230 230
       private final ProfilerController mProfilerController;
    
    231 231
       private final GeckoScreenChangeListener mScreenChangeListener;
    
    232 232
     
    
    233
    +  private TorIntegrationAndroid mTorIntegration;
    
    234
    +
    
    233 235
       private GeckoRuntime() {
    
    234 236
         mWebExtensionController = new WebExtensionController(this);
    
    235 237
         mContentBlockingController = new ContentBlockingController();
    
    ... ... @@ -484,6 +486,8 @@ public final class GeckoRuntime implements Parcelable {
    484 486
           mScreenChangeListener.enable();
    
    485 487
         }
    
    486 488
     
    
    489
    +    mTorIntegration = new TorIntegrationAndroid(context);
    
    490
    +
    
    487 491
         mProfilerController.addMarker(
    
    488 492
             "GeckoView Initialization START", mProfilerController.getProfilerTime());
    
    489 493
         return true;
    
    ... ... @@ -600,6 +604,10 @@ public final class GeckoRuntime implements Parcelable {
    600 604
           mScreenChangeListener.disable();
    
    601 605
         }
    
    602 606
     
    
    607
    +    if (mTorIntegration != null) {
    
    608
    +      mTorIntegration.shutdown();
    
    609
    +    }
    
    610
    +
    
    603 611
         GeckoThread.forceQuit();
    
    604 612
       }
    
    605 613
     
    

  • mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
    ... ... @@ -487,6 +487,11 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
    487 487
           getSettings().mPrioritizeOnions.set(flag);
    
    488 488
           return this;
    
    489 489
         }
    
    490
    +
    
    491
    +    public @NonNull Builder useNewBootstrap(final boolean flag) {
    
    492
    +      getSettings().mUseNewBootstrap.set(flag);
    
    493
    +      return this;
    
    494
    +    }
    
    490 495
       }
    
    491 496
     
    
    492 497
       private GeckoRuntime mRuntime;
    
    ... ... @@ -540,6 +545,8 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
    540 545
           new Pref<>("browser.security_level.security_slider", 4);
    
    541 546
       /* package */ final Pref<Boolean> mPrioritizeOnions =
    
    542 547
           new Pref<>("privacy.prioritizeonions.enabled", false);
    
    548
    +  /* package */ final Pref<Boolean> mUseNewBootstrap =
    
    549
    +      new Pref<>("browser.tor_android.use_new_bootstrap", false);
    
    543 550
     
    
    544 551
       /* package */ int mPreferredColorScheme = COLOR_SCHEME_SYSTEM;
    
    545 552
     
    
    ... ... @@ -1352,6 +1359,15 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
    1352 1359
         return this;
    
    1353 1360
       }
    
    1354 1361
     
    
    1362
    +  public boolean getUseNewBootstrap() {
    
    1363
    +    return mUseNewBootstrap.get();
    
    1364
    +  }
    
    1365
    +
    
    1366
    +  public @NonNull GeckoRuntimeSettings setUseNewBootstrap(final boolean flag) {
    
    1367
    +    mUseNewBootstrap.commit(flag);
    
    1368
    +    return this;
    
    1369
    +  }
    
    1370
    +
    
    1355 1371
       @Override // Parcelable
    
    1356 1372
       public void writeToParcel(final Parcel out, final int flags) {
    
    1357 1373
         super.writeToParcel(out, flags);
    

  • mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
    ... ... @@ -2493,6 +2493,16 @@ public class GeckoSession {
    2493 2493
         return mEventDispatcher.queryBoolean("GeckoView:IsPdfJs");
    
    2494 2494
       }
    
    2495 2495
     
    
    2496
    +  /**
    
    2497
    +   * Try to get last circuit used in this session, if possible.
    
    2498
    +   *
    
    2499
    +   * @return The circuit information as a {@link GeckoResult} object.
    
    2500
    +   */
    
    2501
    +  @AnyThread
    
    2502
    +  public @NonNull GeckoResult<GeckoBundle> getTorCircuit() {
    
    2503
    +    return mEventDispatcher.queryBundle("GeckoView:GetTorCircuit");
    
    2504
    +  }
    
    2505
    +
    
    2496 2506
       /**
    
    2497 2507
        * Set this GeckoSession as active or inactive, which represents if the session is currently
    
    2498 2508
        * visible or not. Setting a GeckoSession to inactive will significantly reduce its memory
    

  • mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java
    1
    +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
    
    2
    + * vim: ts=4 sw=4 expandtab:
    
    3
    + * This Source Code Form is subject to the terms of the Mozilla Public
    
    4
    + * License, v. 2.0. If a copy of the MPL was not distributed with this
    
    5
    + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    
    6
    +
    
    7
    +package org.mozilla.geckoview;
    
    8
    +
    
    9
    +import android.content.Context;
    
    10
    +import android.util.Log;
    
    11
    +
    
    12
    +import java.io.BufferedReader;
    
    13
    +import java.io.File;
    
    14
    +import java.io.IOException;
    
    15
    +import java.io.InputStream;
    
    16
    +import java.io.InputStreamReader;
    
    17
    +import java.nio.file.Files;
    
    18
    +import java.nio.file.Path;
    
    19
    +import java.nio.file.Paths;
    
    20
    +import java.nio.file.StandardCopyOption;
    
    21
    +import java.nio.file.attribute.PosixFilePermission;
    
    22
    +import java.nio.file.attribute.PosixFilePermissions;
    
    23
    +import java.util.ArrayList;
    
    24
    +import java.util.HashMap;
    
    25
    +import java.util.Map;
    
    26
    +import java.util.Set;
    
    27
    +import java.util.UUID;
    
    28
    +
    
    29
    +import org.mozilla.gecko.EventDispatcher;
    
    30
    +import org.mozilla.gecko.GeckoAppShell;
    
    31
    +import org.mozilla.gecko.util.BundleEventListener;
    
    32
    +import org.mozilla.gecko.util.EventCallback;
    
    33
    +import org.mozilla.gecko.util.GeckoBundle;
    
    34
    +
    
    35
    +/* package */ class TorIntegrationAndroid implements BundleEventListener {
    
    36
    +    private static final String TAG = "TorIntegrationAndroid";
    
    37
    +
    
    38
    +    private static final String TOR_EVENT_START = "GeckoView:Tor:StartTor";
    
    39
    +    private static final String TOR_EVENT_STOP = "GeckoView:Tor:StopTor";
    
    40
    +    private static final String MEEK_EVENT_START = "GeckoView:Tor:StartMeek";
    
    41
    +    private static final String MEEK_EVENT_STOP = "GeckoView:Tor:StopMeek";
    
    42
    +
    
    43
    +    private static final String CONTROL_PORT_FILE = "/control-ipc";
    
    44
    +    private static final String SOCKS_FILE = "/socks-ipc";
    
    45
    +    private static final String COOKIE_AUTH_FILE = "/auth-file";
    
    46
    +
    
    47
    +    private final String mLibraryDir;
    
    48
    +    private final Path mCacheDir;
    
    49
    +    private final String mIpcDirectory;
    
    50
    +    private final String mDataDir;
    
    51
    +
    
    52
    +    private TorProcess mTorProcess = null;
    
    53
    +    /**
    
    54
    +     * The first time we run a Tor process in this session, we copy some configuration files to be
    
    55
    +     * sure we always have the latest version, but if we re-launch a tor process we do not need to
    
    56
    +     * copy them again.
    
    57
    +     */
    
    58
    +    private boolean mCopiedConfigFiles = false;
    
    59
    +    /**
    
    60
    +     * Allow multiple proxies to be started, even though it might not actually happen.
    
    61
    +     * The key should be positive (also 0 is not allowed).
    
    62
    +     */
    
    63
    +    private final HashMap<Integer, MeekTransport> mMeeks = new HashMap<>();
    
    64
    +    private int mMeekCounter;
    
    65
    +
    
    66
    +    public TorIntegrationAndroid(Context context) {
    
    67
    +        mLibraryDir = context.getApplicationInfo().nativeLibraryDir;
    
    68
    +        mCacheDir = context.getCacheDir().toPath();
    
    69
    +        mIpcDirectory = mCacheDir + "/tor-private";
    
    70
    +        mDataDir = context.getDataDir().getAbsolutePath() + "/tor";
    
    71
    +        registerListener();
    
    72
    +    }
    
    73
    +
    
    74
    +    public synchronized void shutdown() {
    
    75
    +        // FIXME: It seems this never gets called
    
    76
    +        if (mTorProcess != null) {
    
    77
    +            mTorProcess.shutdown();
    
    78
    +            mTorProcess = null;
    
    79
    +        }
    
    80
    +    }
    
    81
    +
    
    82
    +    private void registerListener() {
    
    83
    +        EventDispatcher.getInstance()
    
    84
    +                .registerUiThreadListener(
    
    85
    +                        this,
    
    86
    +                        TOR_EVENT_START,
    
    87
    +                        MEEK_EVENT_START,
    
    88
    +                        MEEK_EVENT_STOP);
    
    89
    +    }
    
    90
    +
    
    91
    +    @Override // BundleEventListener
    
    92
    +    public synchronized void handleMessage(
    
    93
    +            final String event, final GeckoBundle message, final EventCallback callback) {
    
    94
    +        if (TOR_EVENT_START.equals(event)) {
    
    95
    +            startDaemon(message, callback);
    
    96
    +        } else if (TOR_EVENT_STOP.equals(event)) {
    
    97
    +            stopDaemon(message, callback);
    
    98
    +        } else if (MEEK_EVENT_START.equals(event)) {
    
    99
    +            startMeek(message, callback);
    
    100
    +        } else if (MEEK_EVENT_STOP.equals(event)) {
    
    101
    +            stopMeek(message, callback);
    
    102
    +        }
    
    103
    +    }
    
    104
    +
    
    105
    +    private synchronized void startDaemon(final GeckoBundle message, final EventCallback callback) {
    
    106
    +        // Let JS generate this to possibly reduce the chance of race conditions.
    
    107
    +        String handle = message.getString("handle", "");
    
    108
    +        if (handle.isEmpty()) {
    
    109
    +            Log.e(TAG, "Requested to start a tor process without a handle.");
    
    110
    +            callback.sendError("Expected a handle for the new process.");
    
    111
    +            return;
    
    112
    +        }
    
    113
    +        Log.d(TAG, "Starting the a tor process with handle " + handle);
    
    114
    +
    
    115
    +        TorProcess previousProcess = mTorProcess;
    
    116
    +        if (previousProcess != null) {
    
    117
    +            Log.w(TAG, "We still have a running process: " + previousProcess.getHandle());
    
    118
    +        }
    
    119
    +        mTorProcess = new TorProcess(handle);
    
    120
    +
    
    121
    +        GeckoBundle bundle = new GeckoBundle(3);
    
    122
    +        bundle.putString("controlPortPath", mIpcDirectory + CONTROL_PORT_FILE);
    
    123
    +        bundle.putString("socksPath", mIpcDirectory + SOCKS_FILE);
    
    124
    +        bundle.putString("cookieFilePath", mIpcDirectory + COOKIE_AUTH_FILE);
    
    125
    +        callback.sendSuccess(bundle);
    
    126
    +    }
    
    127
    +
    
    128
    +    private synchronized void stopDaemon(final GeckoBundle message, final EventCallback callback) {
    
    129
    +        if (mTorProcess == null) {
    
    130
    +            if (callback != null) {
    
    131
    +                callback.sendSuccess(null);
    
    132
    +            }
    
    133
    +            return;
    
    134
    +        }
    
    135
    +        String handle = message.getString("handle", "");
    
    136
    +        if (!mTorProcess.getHandle().equals(handle)) {
    
    137
    +            GeckoBundle bundle = new GeckoBundle(1);
    
    138
    +            bundle.putString("error", "The requested process has not been found. It might have already been stopped.");
    
    139
    +            callback.sendError(bundle);
    
    140
    +            return;
    
    141
    +        }
    
    142
    +        mTorProcess.shutdown();
    
    143
    +        mTorProcess = null;
    
    144
    +        callback.sendSuccess(null);
    
    145
    +    }
    
    146
    +
    
    147
    +    class TorProcess extends Thread {
    
    148
    +        private static final String TOR_EVENT_STARTED = "GeckoView:Tor:TorStarted";
    
    149
    +        private static final String TOR_EVENT_START_FAILED = "GeckoView:Tor:TorStartFailed";
    
    150
    +        private static final String TOR_EVENT_EXITED = "GeckoView:Tor:TorExited";
    
    151
    +        private final String mHandle;
    
    152
    +        private Process mProcess = null;
    
    153
    +
    
    154
    +        TorProcess(String handle) {
    
    155
    +            mHandle = handle;
    
    156
    +            setName("tor-process-" + handle);
    
    157
    +            start();
    
    158
    +        }
    
    159
    +
    
    160
    +        @Override
    
    161
    +        public void run() {
    
    162
    +            cleanIpcDirectory();
    
    163
    +
    
    164
    +            final String ipcDir = TorIntegrationAndroid.this.mIpcDirectory;
    
    165
    +            final ArrayList<String> args = new ArrayList<>();
    
    166
    +            args.add(mLibraryDir + "/libTor.so");
    
    167
    +            args.add("DisableNetwork");
    
    168
    +            args.add("1");
    
    169
    +            args.add("+__ControlPort");
    
    170
    +            args.add("unix:" + ipcDir + CONTROL_PORT_FILE);
    
    171
    +            args.add("+__SocksPort");
    
    172
    +            args.add("unix:" + ipcDir + SOCKS_FILE + " IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth");
    
    173
    +            args.add("CookieAuthentication");
    
    174
    +            args.add("1");
    
    175
    +            args.add("CookieAuthFile");
    
    176
    +            args.add(ipcDir + COOKIE_AUTH_FILE);
    
    177
    +            args.add("DataDirectory");
    
    178
    +            args.add(mDataDir);
    
    179
    +            boolean copied = true;
    
    180
    +            try {
    
    181
    +                copyAndUseConfigFile("--defaults-torrc", "torrc-defaults", args);
    
    182
    +            } catch (IOException e) {
    
    183
    +                Log.w(TAG, "torrc-default cannot be created, pluggable transports will not be available", e);
    
    184
    +                copied = false;
    
    185
    +            }
    
    186
    +            try {
    
    187
    +                copyAndUseConfigFile("GeoIPFile", "geoip", args);
    
    188
    +                copyAndUseConfigFile("GeoIPv6File", "geoip6", args);
    
    189
    +            } catch (IOException e) {
    
    190
    +                Log.w(TAG, "GeoIP files cannot be created, this feature will not be available.", e);
    
    191
    +                copied = false;
    
    192
    +            }
    
    193
    +            mCopiedConfigFiles = copied;
    
    194
    +
    
    195
    +            Log.d(TAG, "Starting tor with the follwing args: " + args.toString());
    
    196
    +            final ProcessBuilder builder = new ProcessBuilder(args);
    
    197
    +            builder.directory(new File(mLibraryDir));
    
    198
    +            try {
    
    199
    +                mProcess = builder.start();
    
    200
    +            } catch (IOException e) {
    
    201
    +                Log.e(TAG, "Cannot start tor " + mHandle, e);
    
    202
    +                final GeckoBundle data = new GeckoBundle(2);
    
    203
    +                data.putString("handle", mHandle);
    
    204
    +                data.putString("error", e.getMessage());
    
    205
    +                EventDispatcher.getInstance().dispatch(TOR_EVENT_START_FAILED, data);
    
    206
    +                return;
    
    207
    +            }
    
    208
    +            Log.i(TAG, "Tor process " + mHandle + " started.");
    
    209
    +            {
    
    210
    +                final GeckoBundle data = new GeckoBundle(1);
    
    211
    +                data.putString("handle", mHandle);
    
    212
    +                EventDispatcher.getInstance().dispatch(TOR_EVENT_STARTED, data);
    
    213
    +            }
    
    214
    +            try {
    
    215
    +                BufferedReader reader = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
    
    216
    +                String line;
    
    217
    +                while ((line = reader.readLine()) != null) {
    
    218
    +                    Log.i(TAG, "[tor-" + mHandle + "] " + line);
    
    219
    +                }
    
    220
    +            } catch (IOException e) {
    
    221
    +                Log.e(TAG, "Failed to read stdout of the tor process " + mHandle, e);
    
    222
    +            }
    
    223
    +            Log.d(TAG, "Exiting the stdout loop for process " + mHandle);
    
    224
    +            final GeckoBundle data = new GeckoBundle(2);
    
    225
    +            data.putString("handle", mHandle);
    
    226
    +            try {
    
    227
    +                data.putInt("status", mProcess.waitFor());
    
    228
    +            } catch (InterruptedException e) {
    
    229
    +                Log.e(TAG, "Failed to wait for the tor process " + mHandle, e);
    
    230
    +                data.putInt("status", 0xdeadbeef);
    
    231
    +            }
    
    232
    +            // FIXME: We usually don't reach this when the application is killed!
    
    233
    +            // So, we don't do our cleanup.
    
    234
    +            Log.i(TAG, "Tor process " + mHandle + " has exited.");
    
    235
    +            EventDispatcher.getInstance().dispatch(TOR_EVENT_EXITED, data);
    
    236
    +        }
    
    237
    +
    
    238
    +        private void cleanIpcDirectory() {
    
    239
    +            File directory = new File(TorIntegrationAndroid.this.mIpcDirectory);
    
    240
    +            if (!Files.isDirectory(directory.toPath())) {
    
    241
    +                if (!directory.mkdirs()) {
    
    242
    +                    Log.e(TAG, "Failed to create the IPC directory.");
    
    243
    +                    return;
    
    244
    +                }
    
    245
    +                try {
    
    246
    +                    Set<PosixFilePermission> chmod = PosixFilePermissions.fromString("rwx------");
    
    247
    +                    Files.setPosixFilePermissions(directory.toPath(), chmod);
    
    248
    +                } catch (IOException e) {
    
    249
    +                    Log.e(TAG, "Could not set the permissions to the IPC directory.", e);
    
    250
    +                }
    
    251
    +                return;
    
    252
    +            }
    
    253
    +            // We assume we do not have child directories, only files
    
    254
    +            File[] maybeFiles = directory.listFiles();
    
    255
    +            if (maybeFiles != null) {
    
    256
    +                for (File file : maybeFiles) {
    
    257
    +                    if (!file.delete()) {
    
    258
    +                        Log.d(TAG, "Could not delete " + file);
    
    259
    +                    }
    
    260
    +                }
    
    261
    +            }
    
    262
    +        }
    
    263
    +
    
    264
    +        private void copyAndUseConfigFile(String option, String name, ArrayList<String> args) throws IOException {
    
    265
    +            final Path path = Paths.get(mCacheDir.toFile().getAbsolutePath(), name);
    
    266
    +            if (!mCopiedConfigFiles || !path.toFile().exists()) {
    
    267
    +                final Context context = GeckoAppShell.getApplicationContext();
    
    268
    +                final InputStream in = context.getAssets().open("common/" + name);
    
    269
    +                Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);
    
    270
    +                in.close();
    
    271
    +            }
    
    272
    +            args.add(option);
    
    273
    +            args.add(path.toString());
    
    274
    +        }
    
    275
    +
    
    276
    +        public void shutdown() {
    
    277
    +            if (mProcess != null && mProcess.isAlive()) {
    
    278
    +                mProcess.destroy();
    
    279
    +            }
    
    280
    +            if (isAlive()) {
    
    281
    +                try {
    
    282
    +                    join();
    
    283
    +                } catch (InterruptedException e) {
    
    284
    +                    Log.e(TAG, "Cannot join the thread for tor process " + mHandle + ", possibly already terminated", e);
    
    285
    +                }
    
    286
    +            }
    
    287
    +        }
    
    288
    +
    
    289
    +        public String getHandle() {
    
    290
    +            return mHandle;
    
    291
    +        }
    
    292
    +    }
    
    293
    +
    
    294
    +    private synchronized void startMeek(final GeckoBundle message, final EventCallback callback) {
    
    295
    +        if (callback == null) {
    
    296
    +            Log.e(TAG, "Tried to start Meek without a callback.");
    
    297
    +            return;
    
    298
    +        }
    
    299
    +        mMeekCounter++;
    
    300
    +        mMeeks.put(new Integer(mMeekCounter), new MeekTransport(callback, mMeekCounter));
    
    301
    +    }
    
    302
    +
    
    303
    +    private synchronized void stopMeek(final GeckoBundle message, final EventCallback callback) {
    
    304
    +        final Integer key = message.getInteger("id");
    
    305
    +        final MeekTransport meek = mMeeks.remove(key);
    
    306
    +        if (meek != null) {
    
    307
    +            meek.shutdown();
    
    308
    +        }
    
    309
    +        if (callback != null) {
    
    310
    +            callback.sendSuccess(null);
    
    311
    +        }
    
    312
    +    }
    
    313
    +
    
    314
    +    private class MeekTransport extends Thread {
    
    315
    +        private static final String TRANSPORT = "meek_lite";
    
    316
    +        private Process mProcess;
    
    317
    +        private final EventCallback mCallback;
    
    318
    +        private final int mId;
    
    319
    +
    
    320
    +        MeekTransport(final EventCallback callback, int id) {
    
    321
    +            setName("meek-" + id);
    
    322
    +            final ProcessBuilder builder = new ProcessBuilder(mLibraryDir + "/libObfs4proxy.so");
    
    323
    +            {
    
    324
    +                final Map<String, String> env = builder.environment();
    
    325
    +                env.put("TOR_PT_MANAGED_TRANSPORT_VER", "1");
    
    326
    +                env.put("TOR_PT_STATE_LOCATION", mDataDir + "/pt_state");
    
    327
    +                env.put("TOR_PT_EXIT_ON_STDIN_CLOSE", "1");
    
    328
    +                env.put("TOR_PT_CLIENT_TRANSPORTS", TRANSPORT);
    
    329
    +            }
    
    330
    +            mCallback = callback;
    
    331
    +            mId = id;
    
    332
    +            try {
    
    333
    +                // We expect this process to be short-lived, therefore we do not bother with
    
    334
    +                // implementing this as a service.
    
    335
    +                mProcess = builder.start();
    
    336
    +            } catch (IOException e) {
    
    337
    +                Log.e(TAG, "Cannot start the PT", e);
    
    338
    +                callback.sendError(e.getMessage());
    
    339
    +                return;
    
    340
    +            }
    
    341
    +            start();
    
    342
    +        }
    
    343
    +
    
    344
    +        /**
    
    345
    +         * Parse the standard output of the pluggable transport to find the hostname and port it is
    
    346
    +         * listening on.
    
    347
    +         * <p>
    
    348
    +         * See also the specs for the IPC protocol at https://spec.torproject.org/pt-spec/ipc.html.
    
    349
    +         */
    
    350
    +        @Override
    
    351
    +        public void run() {
    
    352
    +            final String PROTOCOL_VERSION = "1";
    
    353
    +            String hostname = "";
    
    354
    +            boolean valid = false;
    
    355
    +            int port = 0;
    
    356
    +            String error = "Did not see a CMETHOD";
    
    357
    +            try {
    
    358
    +                InputStreamReader isr = new InputStreamReader(mProcess.getInputStream());
    
    359
    +                BufferedReader reader = new BufferedReader(isr);
    
    360
    +                String line;
    
    361
    +                while ((line = reader.readLine()) != null) {
    
    362
    +                    line = line.trim();
    
    363
    +                    Log.d(TAG, "Meek line: " + line);
    
    364
    +                    // Split produces always at least one item
    
    365
    +                    String[] tokens = line.split(" ");
    
    366
    +                    if ("VERSION".equals(tokens[0]) && (tokens.length != 2 || !PROTOCOL_VERSION.equals(tokens[1]))) {
    
    367
    +                        error = "Bad version: " + line;
    
    368
    +                        break;
    
    369
    +                    }
    
    370
    +                    if ("CMETHOD".equals(tokens[0])) {
    
    371
    +                        if (tokens.length != 4) {
    
    372
    +                            error = "Bad number of tokens in CMETHOD: " + line;
    
    373
    +                            break;
    
    374
    +                        }
    
    375
    +                        if (!tokens[1].equals(TRANSPORT)) {
    
    376
    +                            error = "Unexpected transport: " + tokens[1];
    
    377
    +                            break;
    
    378
    +                        }
    
    379
    +                        if (!"socks5".equals(tokens[2])) {
    
    380
    +                            error = "Unexpected proxy type: " + tokens[2];
    
    381
    +                            break;
    
    382
    +                        }
    
    383
    +                        String[] addr = tokens[3].split(":");
    
    384
    +                        if (addr.length != 2) {
    
    385
    +                            error = "Invalid address";
    
    386
    +                            break;
    
    387
    +                        }
    
    388
    +                        hostname = addr[0];
    
    389
    +                        try {
    
    390
    +                            port = Integer.parseInt(addr[1]);
    
    391
    +                        } catch (NumberFormatException e) {
    
    392
    +                            error = "Invalid port: " + e.getMessage();
    
    393
    +                            break;
    
    394
    +                        }
    
    395
    +                        if (port < 1 || port > 65535) {
    
    396
    +                            error = "Invalid port: out of bounds";
    
    397
    +                            break;
    
    398
    +                        }
    
    399
    +                        valid = true;
    
    400
    +                        break;
    
    401
    +                    }
    
    402
    +                    if (tokens[0].endsWith("-ERROR")) {
    
    403
    +                        error = "Seen an error: " + line;
    
    404
    +                        break;
    
    405
    +                    }
    
    406
    +                }
    
    407
    +            } catch (Exception e) {
    
    408
    +                error = e.getMessage();
    
    409
    +            }
    
    410
    +            if (valid) {
    
    411
    +                Log.d(TAG, "Setup a meek transport " + mId + ": " + hostname + ":" + port);
    
    412
    +                final GeckoBundle bundle = new GeckoBundle(3);
    
    413
    +                bundle.putInt("id", mId);
    
    414
    +                bundle.putString("address", hostname);
    
    415
    +                bundle.putInt("port", port);
    
    416
    +                mCallback.sendSuccess(bundle);
    
    417
    +            } else {
    
    418
    +                Log.e(TAG, "Failed to get a usable config from the PT: " + error);
    
    419
    +                mCallback.sendError(error);
    
    420
    +            }
    
    421
    +        }
    
    422
    +
    
    423
    +        void shutdown() {
    
    424
    +            if (mProcess != null) {
    
    425
    +                mProcess.destroy();
    
    426
    +                mProcess = null;
    
    427
    +            }
    
    428
    +            try {
    
    429
    +                join();
    
    430
    +            } catch (InterruptedException e) {
    
    431
    +                Log.e(TAG, "Could not join the meek thread", e);
    
    432
    +            }
    
    433
    +        }
    
    434
    +    }
    
    435
    +}

  • mobile/android/modules/geckoview/GeckoViewContent.sys.mjs
    ... ... @@ -4,6 +4,12 @@
    4 4
     
    
    5 5
     import { GeckoViewModule } from "resource://gre/modules/GeckoViewModule.sys.mjs";
    
    6 6
     
    
    7
    +const lazy = {};
    
    8
    +
    
    9
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    10
    +  TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
    
    11
    +});
    
    12
    +
    
    7 13
     export class GeckoViewContent extends GeckoViewModule {
    
    8 14
       onInit() {
    
    9 15
         this.registerListener([
    
    ... ... @@ -22,6 +28,7 @@ export class GeckoViewContent extends GeckoViewModule {
    22 28
           "GeckoView:UpdateInitData",
    
    23 29
           "GeckoView:ZoomToInput",
    
    24 30
           "GeckoView:IsPdfJs",
    
    31
    +      "GeckoView:GetTorCircuit",
    
    25 32
         ]);
    
    26 33
       }
    
    27 34
     
    
    ... ... @@ -190,6 +197,21 @@ export class GeckoViewContent extends GeckoViewModule {
    190 197
           case "GeckoView:HasCookieBannerRuleForBrowsingContextTree":
    
    191 198
             this._hasCookieBannerRuleForBrowsingContextTree(aCallback);
    
    192 199
             break;
    
    200
    +      case "GeckoView:GetTorCircuit":
    
    201
    +        if (this.browser && aCallback) {
    
    202
    +          const domain = lazy.TorDomainIsolator.getDomainForBrowser(
    
    203
    +            this.browser
    
    204
    +          );
    
    205
    +          const nodes = lazy.TorDomainIsolator.getCircuit(
    
    206
    +            this.browser,
    
    207
    +            domain,
    
    208
    +            this.browser.contentPrincipal.originAttributes.userContextId
    
    209
    +          );
    
    210
    +          aCallback?.onSuccess({ domain, nodes });
    
    211
    +        } else {
    
    212
    +          aCallback?.onSuccess(null);
    
    213
    +        }
    
    214
    +        break;
    
    193 215
         }
    
    194 216
       }
    
    195 217
     
    

  • toolkit/components/tor-launcher/TorProcessAndroid.sys.mjs
    1
    +/* This Source Code Form is subject to the terms of the Mozilla Public
    
    2
    + * License, v. 2.0. If a copy of the MPL was not distributed with this
    
    3
    + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    
    4
    +
    
    5
    +import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
    
    6
    +
    
    7
    +const lazy = {};
    
    8
    +
    
    9
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    10
    +  EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
    
    11
    +});
    
    12
    +
    
    13
    +const logger = new ConsoleAPI({
    
    14
    +  maxLogLevel: "info",
    
    15
    +  prefix: "TorProcessAndroid",
    
    16
    +});
    
    17
    +
    
    18
    +const TorOutgoingEvents = Object.freeze({
    
    19
    +  start: "GeckoView:Tor:StartTor",
    
    20
    +  stop: "GeckoView:Tor:StopTor",
    
    21
    +});
    
    22
    +
    
    23
    +// The events we will listen to
    
    24
    +const TorIncomingEvents = Object.freeze({
    
    25
    +  started: "GeckoView:Tor:TorStarted",
    
    26
    +  startFailed: "GeckoView:Tor:TorStartFailed",
    
    27
    +  exited: "GeckoView:Tor:TorExited",
    
    28
    +});
    
    29
    +
    
    30
    +export class TorProcessAndroid {
    
    31
    +  /**
    
    32
    +   * The handle the Java counterpart uses to refer to the process we started.
    
    33
    +   * We use it to filter the exit events and make sure they refer to the daemon
    
    34
    +   * we are interested in.
    
    35
    +   */
    
    36
    +  #processHandle = null;
    
    37
    +  /**
    
    38
    +   * The promise resolver we call when the Java counterpart sends the event that
    
    39
    +   * tor has started.
    
    40
    +   */
    
    41
    +  #startResolve = null;
    
    42
    +  /**
    
    43
    +   * The promise resolver we call when the Java counterpart sends the event that
    
    44
    +   * it failed to start tor.
    
    45
    +   */
    
    46
    +  #startReject = null;
    
    47
    +
    
    48
    +  onExit = () => {};
    
    49
    +
    
    50
    +  get isRunning() {
    
    51
    +    return !!this.#processHandle;
    
    52
    +  }
    
    53
    +
    
    54
    +  async start() {
    
    55
    +    // Generate the handle on the JS side so that it's ready in case it takes
    
    56
    +    // less to start the process than to propagate the success.
    
    57
    +    this.#processHandle = crypto.randomUUID();
    
    58
    +    logger.info(`Starting new process with handle ${this.#processHandle}`);
    
    59
    +    // Let's declare it immediately, so that the Java side can do its stuff in
    
    60
    +    // an async manner and we avoid possible race conditions (at most we await
    
    61
    +    // an already resolved/rejected promise.
    
    62
    +    const startEventPromise = new Promise((resolve, reject) => {
    
    63
    +      this.#startResolve = resolve;
    
    64
    +      this.#startReject = reject;
    
    65
    +    });
    
    66
    +    lazy.EventDispatcher.instance.registerListener(
    
    67
    +      this,
    
    68
    +      Object.values(TorIncomingEvents)
    
    69
    +    );
    
    70
    +    let config;
    
    71
    +    try {
    
    72
    +      config = await lazy.EventDispatcher.instance.sendRequestForResult({
    
    73
    +        type: TorOutgoingEvents.start,
    
    74
    +        handle: this.#processHandle,
    
    75
    +      });
    
    76
    +      logger.debug("Sent the start event.");
    
    77
    +    } catch (e) {
    
    78
    +      this.forget();
    
    79
    +      throw e;
    
    80
    +    }
    
    81
    +    await startEventPromise;
    
    82
    +    return config;
    
    83
    +  }
    
    84
    +
    
    85
    +  forget() {
    
    86
    +    // Processes usually exit when we close the control port connection to them.
    
    87
    +    logger.trace(`Forgetting process ${this.#processHandle}`);
    
    88
    +    lazy.EventDispatcher.instance.sendRequestForResult({
    
    89
    +      type: TorOutgoingEvents.stop,
    
    90
    +      handle: this.#processHandle,
    
    91
    +    });
    
    92
    +    logger.debug("Sent the start event.");
    
    93
    +    this.#processHandle = null;
    
    94
    +    lazy.EventDispatcher.instance.unregisterListener(
    
    95
    +      this,
    
    96
    +      Object.values(TorIncomingEvents)
    
    97
    +    );
    
    98
    +  }
    
    99
    +
    
    100
    +  onEvent(event, data, callback) {
    
    101
    +    if (data?.handle !== this.#processHandle) {
    
    102
    +      logger.debug(`Ignoring event ${event} with another handle`, data);
    
    103
    +      return;
    
    104
    +    }
    
    105
    +    logger.info(`Received an event ${event}`, data);
    
    106
    +    switch (event) {
    
    107
    +      case TorIncomingEvents.started:
    
    108
    +        this.#startResolve();
    
    109
    +        break;
    
    110
    +      case TorIncomingEvents.startFailed:
    
    111
    +        this.#startReject(new Error(data.error));
    
    112
    +        break;
    
    113
    +      case TorIncomingEvents.exited:
    
    114
    +        this.forget();
    
    115
    +        if (this.#startReject !== null) {
    
    116
    +          this.#startReject();
    
    117
    +        }
    
    118
    +        this.onExit(data.status);
    
    119
    +        break;
    
    120
    +    }
    
    121
    +  }
    
    122
    +}

  • toolkit/components/tor-launcher/TorProvider.sys.mjs
    ... ... @@ -14,6 +14,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
    14 14
       FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
    
    15 15
       TorController: "resource://gre/modules/TorControlPort.sys.mjs",
    
    16 16
       TorProcess: "resource://gre/modules/TorProcess.sys.mjs",
    
    17
    +  TorProcessAndroid: "resource://gre/modules/TorProcessAndroid.sys.mjs",
    
    17 18
     });
    
    18 19
     
    
    19 20
     const logger = new ConsoleAPI({
    
    ... ... @@ -182,8 +183,12 @@ export class TorProvider {
    182 183
         logger.debug("Initializing the Tor provider.");
    
    183 184
     
    
    184 185
         // These settings might be customized in the following steps.
    
    185
    -    this.#socksSettings = TorLauncherUtil.getPreferredSocksConfiguration();
    
    186
    -    logger.debug("Requested SOCKS configuration", this.#socksSettings);
    
    186
    +    if (TorLauncherUtil.isAndroid) {
    
    187
    +      this.#socksSettings = { transproxy: false };
    
    188
    +    } else {
    
    189
    +      this.#socksSettings = TorLauncherUtil.getPreferredSocksConfiguration();
    
    190
    +      logger.debug("Requested SOCKS configuration", this.#socksSettings);
    
    191
    +    }
    
    187 192
     
    
    188 193
         try {
    
    189 194
           await this.#setControlPortConfiguration();
    
    ... ... @@ -490,10 +495,14 @@ export class TorProvider {
    490 495
           return;
    
    491 496
         }
    
    492 497
     
    
    493
    -    this.#torProcess = new lazy.TorProcess(
    
    494
    -      this.#controlPortSettings,
    
    495
    -      this.#socksSettings
    
    496
    -    );
    
    498
    +    if (TorLauncherUtil.isAndroid) {
    
    499
    +      this.#torProcess = new lazy.TorProcessAndroid();
    
    500
    +    } else {
    
    501
    +      this.#torProcess = new lazy.TorProcess(
    
    502
    +        this.#controlPortSettings,
    
    503
    +        this.#socksSettings
    
    504
    +      );
    
    505
    +    }
    
    497 506
         // Use a closure instead of bind because we reassign #cancelConnection.
    
    498 507
         // Also, we now assign an exit handler that cancels the first connection,
    
    499 508
         // so that a sudden exit before the first connection is completed might
    
    ... ... @@ -507,7 +516,17 @@ export class TorProvider {
    507 516
         };
    
    508 517
     
    
    509 518
         logger.debug("Trying to start the tor process.");
    
    510
    -    await this.#torProcess.start();
    
    519
    +    const res = await this.#torProcess.start();
    
    520
    +    if (TorLauncherUtil.isAndroid) {
    
    521
    +      this.#controlPortSettings = {
    
    522
    +        ipcFile: new lazy.FileUtils.File(res.controlPortPath),
    
    523
    +        cookieFilePath: res.cookieFilePath,
    
    524
    +      };
    
    525
    +      this.#socksSettings = {
    
    526
    +        transproxy: false,
    
    527
    +        ipcFile: new lazy.FileUtils.File(res.socksPath),
    
    528
    +      };
    
    529
    +    }
    
    511 530
         logger.info("Started a tor process");
    
    512 531
       }
    
    513 532
     
    
    ... ... @@ -521,6 +540,11 @@ export class TorProvider {
    521 540
         logger.debug("Reading the control port configuration");
    
    522 541
         const settings = {};
    
    523 542
     
    
    543
    +    if (TorLauncherUtil.isAndroid) {
    
    544
    +      // We will populate the settings after having started the daemon.
    
    545
    +      return;
    
    546
    +    }
    
    547
    +
    
    524 548
         const isWindows = Services.appinfo.OS === "WINNT";
    
    525 549
         // Determine how Tor Launcher will connect to the Tor control port.
    
    526 550
         // Environment variables get top priority followed by preferences.
    

  • toolkit/components/tor-launcher/moz.build
    ... ... @@ -5,6 +5,7 @@ EXTRA_JS_MODULES += [
    5 5
         "TorLauncherUtil.sys.mjs",
    
    6 6
         "TorParsers.sys.mjs",
    
    7 7
         "TorProcess.sys.mjs",
    
    8
    +    "TorProcessAndroid.sys.mjs",
    
    8 9
         "TorProvider.sys.mjs",
    
    9 10
         "TorProviderBuilder.sys.mjs",
    
    10 11
         "TorStartupService.sys.mjs",
    

  • toolkit/modules/Moat.sys.mjs
    ... ... @@ -10,6 +10,7 @@ import {
    10 10
     const lazy = {};
    
    11 11
     
    
    12 12
     ChromeUtils.defineESModuleGetters(lazy, {
    
    13
    +  EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
    
    13 14
       Subprocess: "resource://gre/modules/Subprocess.sys.mjs",
    
    14 15
       TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
    
    15 16
       TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    ... ... @@ -290,6 +291,48 @@ class MeekTransport {
    290 291
       }
    
    291 292
     }
    
    292 293
     
    
    294
    +class MeekTransportAndroid {
    
    295
    +  // These members are used by consumers to setup the proxy to do requests over
    
    296
    +  // meek. They are passed to newProxyInfoWithAuth.
    
    297
    +  proxyType = null;
    
    298
    +  proxyAddress = null;
    
    299
    +  proxyPort = 0;
    
    300
    +  proxyUsername = null;
    
    301
    +  proxyPassword = null;
    
    302
    +
    
    303
    +  #id = 0;
    
    304
    +
    
    305
    +  async init() {
    
    306
    +    // ensure we haven't already init'd
    
    307
    +    if (this.#id) {
    
    308
    +      throw new Error("MeekTransport: Already initialized");
    
    309
    +    }
    
    310
    +    const details = await lazy.EventDispatcher.instance.sendRequestForResult({
    
    311
    +      type: "GeckoView:Tor:StartMeek",
    
    312
    +    });
    
    313
    +    this.#id = details.id;
    
    314
    +    this.proxyType = "socks";
    
    315
    +    this.proxyAddress = details.address;
    
    316
    +    this.proxyPort = details.port;
    
    317
    +    [this.proxyUsername, this.proxyPassword] = makeMeekCredentials(
    
    318
    +      this.proxyType
    
    319
    +    );
    
    320
    +  }
    
    321
    +
    
    322
    +  async uninit() {
    
    323
    +    lazy.EventDispatcher.instance.sendRequest({
    
    324
    +      type: "GeckoView:Tor:StopMeek",
    
    325
    +      id: this.#id,
    
    326
    +    });
    
    327
    +    this.#id = 0;
    
    328
    +    this.proxyType = null;
    
    329
    +    this.proxyAddress = null;
    
    330
    +    this.proxyPort = 0;
    
    331
    +    this.proxyUsername = null;
    
    332
    +    this.proxyPassword = null;
    
    333
    +  }
    
    334
    +}
    
    335
    +
    
    293 336
     //
    
    294 337
     // Callback object with a cached promise for the returned Moat data
    
    295 338
     //
    
    ... ... @@ -407,7 +450,10 @@ export class MoatRPC {
    407 450
           throw new Error("MoatRPC: Already initialized");
    
    408 451
         }
    
    409 452
     
    
    410
    -    const meekTransport = new MeekTransport();
    
    453
    +    const meekTransport =
    
    454
    +      Services.appinfo.OS === "Android"
    
    455
    +        ? new MeekTransportAndroid()
    
    456
    +        : new MeekTransport();
    
    411 457
         await meekTransport.init();
    
    412 458
         this.#meekTransport = meekTransport;
    
    413 459
         this.#inited = true;
    

  • toolkit/modules/TorAndroidIntegration.sys.mjs
    1
    +/* This Source Code Form is subject to the terms of the Mozilla Public
    
    2
    + * License, v. 2.0. If a copy of the MPL was not distributed with this
    
    3
    + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    
    4
    +
    
    5
    +import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
    
    6
    +
    
    7
    +const lazy = {};
    
    8
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    9
    +  EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
    
    10
    +  TorConnect: "resource://gre/modules/TorConnect.sys.mjs",
    
    11
    +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    12
    +  TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
    
    13
    +});
    
    14
    +
    
    15
    +const Prefs = Object.freeze({
    
    16
    +  useNewBootstrap: "browser.tor_android.use_new_bootstrap",
    
    17
    +  logLevel: "browser.tor_android.log_level",
    
    18
    +});
    
    19
    +
    
    20
    +const logger = new ConsoleAPI({
    
    21
    +  maxLogLevel: "info",
    
    22
    +  maxLogLevelPref: Prefs.logLevel,
    
    23
    +  prefix: "TorAndroidIntegration",
    
    24
    +});
    
    25
    +
    
    26
    +const ListenedEvents = Object.freeze({
    
    27
    +  settingsGet: "GeckoView:Tor:SettingsGet",
    
    28
    +  settingsSet: "GeckoView:Tor:SettingsSet",
    
    29
    +  settingsApply: "GeckoView:Tor:SettingsApply",
    
    30
    +  settingsSave: "GeckoView:Tor:SettingsSave",
    
    31
    +});
    
    32
    +
    
    33
    +class TorAndroidIntegrationImpl {
    
    34
    +  #initialized = false;
    
    35
    +
    
    36
    +  init() {
    
    37
    +    lazy.EventDispatcher.instance.registerListener(
    
    38
    +      this,
    
    39
    +      Object.values(ListenedEvents)
    
    40
    +    );
    
    41
    +
    
    42
    +    this.#bootstrapMethodReset();
    
    43
    +    Services.prefs.addObserver(Prefs.useNewBootstrap, this);
    
    44
    +  }
    
    45
    +
    
    46
    +  async #initNewBootstrap() {
    
    47
    +    if (this.#initialized) {
    
    48
    +      return;
    
    49
    +    }
    
    50
    +    this.#initialized = true;
    
    51
    +
    
    52
    +    lazy.TorProviderBuilder.init().finally(() => {
    
    53
    +      lazy.TorProviderBuilder.firstWindowLoaded();
    
    54
    +    });
    
    55
    +    try {
    
    56
    +      await lazy.TorSettings.init();
    
    57
    +      await lazy.TorConnect.init();
    
    58
    +    } catch (e) {
    
    59
    +      logger.error("Cannot initialize TorSettings or TorConnect", e);
    
    60
    +    }
    
    61
    +  }
    
    62
    +
    
    63
    +  observe(subj, topic, data) {
    
    64
    +    switch (topic) {
    
    65
    +      case "nsPref:changed":
    
    66
    +        if (data === Prefs.useNewBootstrap) {
    
    67
    +          this.#bootstrapMethodReset();
    
    68
    +        }
    
    69
    +        break;
    
    70
    +    }
    
    71
    +  }
    
    72
    +
    
    73
    +  async onEvent(event, data, callback) {
    
    74
    +    logger.debug(`Received event ${event}`, data);
    
    75
    +    try {
    
    76
    +      switch (event) {
    
    77
    +        case settingsGet:
    
    78
    +          callback?.onSuccess(lazy.TorSettings.getSettings());
    
    79
    +          return;
    
    80
    +        case settingsSet:
    
    81
    +          // This does not throw, so we do not have any way to report the error!
    
    82
    +          lazy.TorSettings.setSettings(data);
    
    83
    +          break;
    
    84
    +        case settingsApply:
    
    85
    +          await lazy.TorSettings.applySettings();
    
    86
    +          break;
    
    87
    +        case settingsSave:
    
    88
    +          await lazy.TorSettings.saveSettings();
    
    89
    +          break;
    
    90
    +      }
    
    91
    +      callback?.onSuccess();
    
    92
    +    } catch (e) {
    
    93
    +      logger.error();
    
    94
    +      callback?.sendError(e);
    
    95
    +    }
    
    96
    +  }
    
    97
    +
    
    98
    +  #bootstrapMethodReset() {
    
    99
    +    if (Services.prefs.getBoolPref(Prefs.useNewBootstrap, false)) {
    
    100
    +      this.#initNewBootstrap();
    
    101
    +    } else {
    
    102
    +      Services.prefs.clearUserPref("network.proxy.socks");
    
    103
    +      Services.prefs.clearUserPref("network.proxy.socks_port");
    
    104
    +    }
    
    105
    +  }
    
    106
    +}
    
    107
    +
    
    108
    +export const TorAndroidIntegration = new TorAndroidIntegrationImpl();

  • toolkit/modules/TorConnect.sys.mjs
    ... ... @@ -793,6 +793,9 @@ export const TorConnect = (() => {
    793 793
     
    
    794 794
                   TorConnect._errorMessage = errorMessage;
    
    795 795
                   TorConnect._errorDetails = errorDetails;
    
    796
    +              console.error(
    
    797
    +                `[TorConnect] Entering error state (${errorMessage}, ${errorDetails})`
    
    798
    +              );
    
    796 799
     
    
    797 800
                   Services.obs.notifyObservers(
    
    798 801
                     { message: errorMessage, details: errorDetails },
    

  • toolkit/modules/moz.build
    ... ... @@ -215,6 +215,7 @@ EXTRA_JS_MODULES += [
    215 215
         "Sqlite.sys.mjs",
    
    216 216
         "SubDialog.sys.mjs",
    
    217 217
         "Timer.sys.mjs",
    
    218
    +    "TorAndroidIntegration.sys.mjs",
    
    218 219
         "TorConnect.sys.mjs",
    
    219 220
         "TorSettings.sys.mjs",
    
    220 221
         "TorStrings.sys.mjs",
    

  • _______________________________________________
    tor-commits mailing list
    tor-commits@xxxxxxxxxxxxxxxxxxxx
    https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits