Pier Angelo Vendrame pushed to branch tor-browser-115.6.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
-
1edf6cd0
by Pier Angelo Vendrame at 2023-12-20T17:50:17+00:00
-
e9cc4840
by Dan Ballard at 2023-12-20T17:50:17+00:00
6 changed files:
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java
- + mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorSettings.java
- + mobile/android/geckoview/src/main/java/org/mozilla/geckoview/androidlegacysettings/Prefs.java
- + mobile/android/geckoview/src/main/java/org/mozilla/geckoview/androidlegacysettings/TorLegacyAndroidSettings.java
- toolkit/modules/TorAndroidIntegration.sys.mjs
Changes:
| ... | ... | @@ -1008,6 +1008,14 @@ public final class GeckoRuntime implements Parcelable { |
| 1008 | 1008 | return mPushController;
|
| 1009 | 1009 | }
|
| 1010 | 1010 | |
| 1011 | + /**
|
|
| 1012 | + * Get the Tor integration controller for this runtime.
|
|
| 1013 | + */
|
|
| 1014 | + @UiThread
|
|
| 1015 | + public @NonNull TorIntegrationAndroid getTorIntegrationController() {
|
|
| 1016 | + return mTorIntegration;
|
|
| 1017 | + }
|
|
| 1018 | + |
|
| 1011 | 1019 | /**
|
| 1012 | 1020 | * Appends notes to crash report.
|
| 1013 | 1021 | *
|
| ... | ... | @@ -9,6 +9,10 @@ package org.mozilla.geckoview; |
| 9 | 9 | import android.content.Context;
|
| 10 | 10 | import android.util.Log;
|
| 11 | 11 | |
| 12 | +import androidx.annotation.AnyThread;
|
|
| 13 | +import androidx.annotation.NonNull;
|
|
| 14 | +import androidx.annotation.Nullable;
|
|
| 15 | + |
|
| 12 | 16 | import java.io.BufferedReader;
|
| 13 | 17 | import java.io.File;
|
| 14 | 18 | import java.io.IOException;
|
| ... | ... | @@ -22,9 +26,9 @@ import java.nio.file.attribute.PosixFilePermission; |
| 22 | 26 | import java.nio.file.attribute.PosixFilePermissions;
|
| 23 | 27 | import java.util.ArrayList;
|
| 24 | 28 | import java.util.HashMap;
|
| 29 | +import java.util.HashSet;
|
|
| 25 | 30 | import java.util.Map;
|
| 26 | 31 | import java.util.Set;
|
| 27 | -import java.util.UUID;
|
|
| 28 | 32 | |
| 29 | 33 | import org.mozilla.gecko.EventDispatcher;
|
| 30 | 34 | import org.mozilla.gecko.GeckoAppShell;
|
| ... | ... | @@ -32,13 +36,27 @@ import org.mozilla.gecko.util.BundleEventListener; |
| 32 | 36 | import org.mozilla.gecko.util.EventCallback;
|
| 33 | 37 | import org.mozilla.gecko.util.GeckoBundle;
|
| 34 | 38 | |
| 35 | -/* package */ class TorIntegrationAndroid implements BundleEventListener {
|
|
| 39 | +import org.mozilla.geckoview.androidlegacysettings.TorLegacyAndroidSettings;
|
|
| 40 | + |
|
| 41 | +public class TorIntegrationAndroid implements BundleEventListener {
|
|
| 36 | 42 | private static final String TAG = "TorIntegrationAndroid";
|
| 37 | 43 | |
| 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";
|
|
| 44 | + // Events we listen to
|
|
| 45 | + private static final String EVENT_TOR_START = "GeckoView:Tor:StartTor";
|
|
| 46 | + private static final String EVENT_TOR_STOP = "GeckoView:Tor:StopTor";
|
|
| 47 | + private static final String EVENT_MEEK_START = "GeckoView:Tor:StartMeek";
|
|
| 48 | + private static final String EVENT_MEEK_STOP = "GeckoView:Tor:StopMeek";
|
|
| 49 | + |
|
| 50 | + // Events we emit
|
|
| 51 | + private static final String EVENT_SETTINGS_GET = "GeckoView:Tor:SettingsGet";
|
|
| 52 | + private static final String EVENT_SETTINGS_SET = "GeckoView:Tor:SettingsSet";
|
|
| 53 | + private static final String EVENT_SETTINGS_APPLY = "GeckoView:Tor:SettingsApply";
|
|
| 54 | + private static final String EVENT_SETTINGS_SAVE = "GeckoView:Tor:SettingsSave";
|
|
| 55 | + private static final String EVENT_BOOTSTRAP_BEGIN = "GeckoView:Tor:BootstrapBegin";
|
|
| 56 | + private static final String EVENT_BOOTSTRAP_BEGIN_AUTO = "GeckoView:Tor:BootstrapBeginAuto";
|
|
| 57 | + private static final String EVENT_BOOTSTRAP_CANCEL = "GeckoView:Tor:BootstrapCancel";
|
|
| 58 | + private static final String EVENT_BOOTSTRAP_GET_STATE = "GeckoView:Tor:BootstrapGetState";
|
|
| 59 | + private static final String EVENT_SETTINGS_READY = "GeckoView:Tor:SettingsReady";
|
|
| 42 | 60 | |
| 43 | 61 | private static final String CONTROL_PORT_FILE = "/control-ipc";
|
| 44 | 62 | private static final String SOCKS_FILE = "/socks-ipc";
|
| ... | ... | @@ -63,7 +81,9 @@ import org.mozilla.gecko.util.GeckoBundle; |
| 63 | 81 | private final HashMap<Integer, MeekTransport> mMeeks = new HashMap<>();
|
| 64 | 82 | private int mMeekCounter;
|
| 65 | 83 | |
| 66 | - public TorIntegrationAndroid(Context context) {
|
|
| 84 | + private TorSettings mSettings = null;
|
|
| 85 | + |
|
| 86 | + /* package */ TorIntegrationAndroid(Context context) {
|
|
| 67 | 87 | mLibraryDir = context.getApplicationInfo().nativeLibraryDir;
|
| 68 | 88 | mCacheDir = context.getCacheDir().toPath();
|
| 69 | 89 | mIpcDirectory = mCacheDir + "/tor-private";
|
| ... | ... | @@ -71,7 +91,7 @@ import org.mozilla.gecko.util.GeckoBundle; |
| 71 | 91 | registerListener();
|
| 72 | 92 | }
|
| 73 | 93 | |
| 74 | - public synchronized void shutdown() {
|
|
| 94 | + /* package */ synchronized void shutdown() {
|
|
| 75 | 95 | // FIXME: It seems this never gets called
|
| 76 | 96 | if (mTorProcess != null) {
|
| 77 | 97 | mTorProcess.shutdown();
|
| ... | ... | @@ -83,22 +103,36 @@ import org.mozilla.gecko.util.GeckoBundle; |
| 83 | 103 | EventDispatcher.getInstance()
|
| 84 | 104 | .registerUiThreadListener(
|
| 85 | 105 | this,
|
| 86 | - TOR_EVENT_START,
|
|
| 87 | - MEEK_EVENT_START,
|
|
| 88 | - MEEK_EVENT_STOP);
|
|
| 106 | + EVENT_TOR_START,
|
|
| 107 | + EVENT_MEEK_START,
|
|
| 108 | + EVENT_MEEK_STOP,
|
|
| 109 | + EVENT_SETTINGS_READY);
|
|
| 89 | 110 | }
|
| 90 | 111 | |
| 91 | 112 | @Override // BundleEventListener
|
| 92 | 113 | public synchronized void handleMessage(
|
| 93 | 114 | final String event, final GeckoBundle message, final EventCallback callback) {
|
| 94 | - if (TOR_EVENT_START.equals(event)) {
|
|
| 115 | + if (EVENT_TOR_START.equals(event)) {
|
|
| 95 | 116 | startDaemon(message, callback);
|
| 96 | - } else if (TOR_EVENT_STOP.equals(event)) {
|
|
| 117 | + } else if (EVENT_TOR_STOP.equals(event)) {
|
|
| 97 | 118 | stopDaemon(message, callback);
|
| 98 | - } else if (MEEK_EVENT_START.equals(event)) {
|
|
| 119 | + } else if (EVENT_MEEK_START.equals(event)) {
|
|
| 99 | 120 | startMeek(message, callback);
|
| 100 | - } else if (MEEK_EVENT_STOP.equals(event)) {
|
|
| 121 | + } else if (EVENT_MEEK_STOP.equals(event)) {
|
|
| 101 | 122 | stopMeek(message, callback);
|
| 123 | + } else if (EVENT_SETTINGS_READY.equals(event)) {
|
|
| 124 | + loadSettings(message);
|
|
| 125 | + }
|
|
| 126 | + }
|
|
| 127 | + |
|
| 128 | + private void loadSettings(GeckoBundle message) {
|
|
| 129 | + if (TorLegacyAndroidSettings.unmigrated()) {
|
|
| 130 | + mSettings = TorLegacyAndroidSettings.loadTorSettings();
|
|
| 131 | + setSettings(mSettings);
|
|
| 132 | + TorLegacyAndroidSettings.setMigrated();
|
|
| 133 | + } else {
|
|
| 134 | + GeckoBundle bundle = message.getBundle("settings");
|
|
| 135 | + mSettings = new TorSettings(bundle);
|
|
| 102 | 136 | }
|
| 103 | 137 | }
|
| 104 | 138 | |
| ... | ... | @@ -145,9 +179,9 @@ import org.mozilla.gecko.util.GeckoBundle; |
| 145 | 179 | }
|
| 146 | 180 | |
| 147 | 181 | 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";
|
|
| 182 | + private static final String EVENT_TOR_STARTED = "GeckoView:Tor:TorStarted";
|
|
| 183 | + private static final String EVENT_TOR_START_FAILED = "GeckoView:Tor:TorStartFailed";
|
|
| 184 | + private static final String EVENT_TOR_EXITED = "GeckoView:Tor:TorExited";
|
|
| 151 | 185 | private final String mHandle;
|
| 152 | 186 | private Process mProcess = null;
|
| 153 | 187 | |
| ... | ... | @@ -202,14 +236,14 @@ import org.mozilla.gecko.util.GeckoBundle; |
| 202 | 236 | final GeckoBundle data = new GeckoBundle(2);
|
| 203 | 237 | data.putString("handle", mHandle);
|
| 204 | 238 | data.putString("error", e.getMessage());
|
| 205 | - EventDispatcher.getInstance().dispatch(TOR_EVENT_START_FAILED, data);
|
|
| 239 | + EventDispatcher.getInstance().dispatch(EVENT_TOR_START_FAILED, data);
|
|
| 206 | 240 | return;
|
| 207 | 241 | }
|
| 208 | 242 | Log.i(TAG, "Tor process " + mHandle + " started.");
|
| 209 | 243 | {
|
| 210 | 244 | final GeckoBundle data = new GeckoBundle(1);
|
| 211 | 245 | data.putString("handle", mHandle);
|
| 212 | - EventDispatcher.getInstance().dispatch(TOR_EVENT_STARTED, data);
|
|
| 246 | + EventDispatcher.getInstance().dispatch(EVENT_TOR_STARTED, data);
|
|
| 213 | 247 | }
|
| 214 | 248 | try {
|
| 215 | 249 | BufferedReader reader = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
|
| ... | ... | @@ -232,7 +266,7 @@ import org.mozilla.gecko.util.GeckoBundle; |
| 232 | 266 | // FIXME: We usually don't reach this when the application is killed!
|
| 233 | 267 | // So, we don't do our cleanup.
|
| 234 | 268 | Log.i(TAG, "Tor process " + mHandle + " has exited.");
|
| 235 | - EventDispatcher.getInstance().dispatch(TOR_EVENT_EXITED, data);
|
|
| 269 | + EventDispatcher.getInstance().dispatch(EVENT_TOR_EXITED, data);
|
|
| 236 | 270 | }
|
| 237 | 271 | |
| 238 | 272 | private void cleanIpcDirectory() {
|
| ... | ... | @@ -432,4 +466,71 @@ import org.mozilla.gecko.util.GeckoBundle; |
| 432 | 466 | }
|
| 433 | 467 | }
|
| 434 | 468 | }
|
| 469 | + |
|
| 470 | + public static class BootstrapState {
|
|
| 471 | + // FIXME: We can do better than this :)
|
|
| 472 | + public GeckoBundle mBundle;
|
|
| 473 | + |
|
| 474 | + BootstrapState(GeckoBundle bundle) {
|
|
| 475 | + mBundle = bundle;
|
|
| 476 | + }
|
|
| 477 | + }
|
|
| 478 | + |
|
| 479 | + public interface BootstrapStateChangeListener {
|
|
| 480 | + void onBootstrapStateChange(BootstrapState state);
|
|
| 481 | + }
|
|
| 482 | + |
|
| 483 | + public @NonNull GeckoResult<GeckoBundle> getSettings() {
|
|
| 484 | + return EventDispatcher.getInstance().queryBundle(EVENT_SETTINGS_GET);
|
|
| 485 | + }
|
|
| 486 | + |
|
| 487 | + public @NonNull GeckoResult<Void> setSettings(final TorSettings settings) {
|
|
| 488 | + return EventDispatcher.getInstance().queryVoid(EVENT_SETTINGS_SET, settings.asGeckoBundle());
|
|
| 489 | + }
|
|
| 490 | + |
|
| 491 | + public @NonNull GeckoResult<Void> applySettings() {
|
|
| 492 | + return EventDispatcher.getInstance().queryVoid(EVENT_SETTINGS_APPLY);
|
|
| 493 | + }
|
|
| 494 | + |
|
| 495 | + public @NonNull GeckoResult<Void> saveSettings() {
|
|
| 496 | + return EventDispatcher.getInstance().queryVoid(EVENT_SETTINGS_SAVE);
|
|
| 497 | + }
|
|
| 498 | + |
|
| 499 | + public @NonNull GeckoResult<Void> beginBootstrap() {
|
|
| 500 | + return EventDispatcher.getInstance().queryVoid(EVENT_BOOTSTRAP_BEGIN);
|
|
| 501 | + }
|
|
| 502 | + |
|
| 503 | + public @NonNull GeckoResult<Void> beginAutoBootstrap(final String countryCode) {
|
|
| 504 | + final GeckoBundle bundle = new GeckoBundle(1);
|
|
| 505 | + bundle.putString("countryCode", countryCode);
|
|
| 506 | + return EventDispatcher.getInstance().queryVoid(EVENT_BOOTSTRAP_BEGIN_AUTO, bundle);
|
|
| 507 | + }
|
|
| 508 | + |
|
| 509 | + public @NonNull GeckoResult<Void> beginAutoBootstrap() {
|
|
| 510 | + return beginAutoBootstrap(null);
|
|
| 511 | + }
|
|
| 512 | + |
|
| 513 | + public @NonNull GeckoResult<Void> cancelBootstrap() {
|
|
| 514 | + return EventDispatcher.getInstance().queryVoid(EVENT_BOOTSTRAP_CANCEL);
|
|
| 515 | + }
|
|
| 516 | + |
|
| 517 | + public @NonNull GeckoResult<BootstrapState> getBootstrapState() {
|
|
| 518 | + return EventDispatcher.getInstance().queryBundle(EVENT_BOOTSTRAP_GET_STATE).map(new GeckoResult.OnValueMapper<>() {
|
|
| 519 | + @AnyThread
|
|
| 520 | + @Nullable
|
|
| 521 | + public BootstrapState onValue(@Nullable GeckoBundle value) throws Throwable {
|
|
| 522 | + return new BootstrapState(value);
|
|
| 523 | + }
|
|
| 524 | + });
|
|
| 525 | + }
|
|
| 526 | + |
|
| 527 | + public void registerBootstrapStateChangeListener(BootstrapStateChangeListener listener) {
|
|
| 528 | + mBootstrapStateListeners.add(listener);
|
|
| 529 | + }
|
|
| 530 | + |
|
| 531 | + public void unregisterBootstrapStateChangeListener(BootstrapStateChangeListener listener) {
|
|
| 532 | + mBootstrapStateListeners.remove(listener);
|
|
| 533 | + }
|
|
| 534 | + |
|
| 535 | + private final HashSet<BootstrapStateChangeListener> mBootstrapStateListeners = new HashSet<>();
|
|
| 435 | 536 | } |
| 1 | +package org.mozilla.geckoview;
|
|
| 2 | + |
|
| 3 | +import android.util.Log;
|
|
| 4 | + |
|
| 5 | +import org.json.JSONArray;
|
|
| 6 | +import org.json.JSONObject;
|
|
| 7 | +import org.mozilla.gecko.util.GeckoBundle;
|
|
| 8 | + |
|
| 9 | +import java.io.ByteArrayInputStream;
|
|
| 10 | +import java.io.File;
|
|
| 11 | +import java.io.FileOutputStream;
|
|
| 12 | +import java.io.IOException;
|
|
| 13 | +import java.io.InputStream;
|
|
| 14 | +import java.io.PrintStream;
|
|
| 15 | +import java.io.SequenceInputStream;
|
|
| 16 | +import java.io.UnsupportedEncodingException;
|
|
| 17 | +import java.util.ArrayList;
|
|
| 18 | +import java.util.Arrays;
|
|
| 19 | +import java.util.List;
|
|
| 20 | +import java.util.Locale;
|
|
| 21 | +import java.util.stream.Collectors;
|
|
| 22 | + |
|
| 23 | +public class TorSettings {
|
|
| 24 | + |
|
| 25 | + public enum BridgeSource {
|
|
| 26 | + Invalid(-1),
|
|
| 27 | + BuiltIn(0),
|
|
| 28 | + BridgeDB(1),
|
|
| 29 | + UserProvided(2);
|
|
| 30 | + |
|
| 31 | + private int source;
|
|
| 32 | + |
|
| 33 | + BridgeSource(final int source) {
|
|
| 34 | + this.source = source;
|
|
| 35 | + }
|
|
| 36 | + |
|
| 37 | + public static BridgeSource fromInt(int i) {
|
|
| 38 | + switch (i) {
|
|
| 39 | + case -1: return Invalid;
|
|
| 40 | + case 0: return BuiltIn;
|
|
| 41 | + case 1: return BridgeDB;
|
|
| 42 | + case 2: return UserProvided;
|
|
| 43 | + }
|
|
| 44 | + return Invalid;
|
|
| 45 | + }
|
|
| 46 | + |
|
| 47 | + public int toInt() {
|
|
| 48 | + return this.source;
|
|
| 49 | + }
|
|
| 50 | + }
|
|
| 51 | + |
|
| 52 | + public enum ProxyType {
|
|
| 53 | + Invalid(-1),
|
|
| 54 | + Socks4(0),
|
|
| 55 | + Socks5(1),
|
|
| 56 | + HTTPS(2);
|
|
| 57 | + |
|
| 58 | + private int type;
|
|
| 59 | + |
|
| 60 | + ProxyType(final int type) {
|
|
| 61 | + this.type = type;
|
|
| 62 | + }
|
|
| 63 | + |
|
| 64 | + public int toInt() {
|
|
| 65 | + return type;
|
|
| 66 | + }
|
|
| 67 | + |
|
| 68 | + public static ProxyType fromInt(int i) {
|
|
| 69 | + switch (i) {
|
|
| 70 | + case -1: return Invalid;
|
|
| 71 | + case 0: return Socks4;
|
|
| 72 | + case 1: return Socks5;
|
|
| 73 | + case 2: return HTTPS;
|
|
| 74 | + }
|
|
| 75 | + return Invalid;
|
|
| 76 | + }
|
|
| 77 | + }
|
|
| 78 | + |
|
| 79 | + private boolean loaded = false;
|
|
| 80 | + |
|
| 81 | + public boolean enabled = true;
|
|
| 82 | + |
|
| 83 | + public boolean quickstart = false;
|
|
| 84 | + |
|
| 85 | + // bridges section
|
|
| 86 | + public boolean bridgesEnabled = false;
|
|
| 87 | + public BridgeSource bridgesSource = BridgeSource.Invalid;
|
|
| 88 | + public String bridgesBuiltinType = "";
|
|
| 89 | + public String[] bridgeBridgeStrings;
|
|
| 90 | + |
|
| 91 | + // proxy section
|
|
| 92 | + public boolean proxyEnabled = false;
|
|
| 93 | + public ProxyType proxyType = ProxyType.Invalid;
|
|
| 94 | + public String proxyAddress = "";
|
|
| 95 | + public int proxyPort = 0;
|
|
| 96 | + public String proxyUsername = "";
|
|
| 97 | + public String proxyPassword = "";
|
|
| 98 | + |
|
| 99 | + // firewall section
|
|
| 100 | + public boolean firewallEnabled = false;
|
|
| 101 | + public int[] firewallAllowedPorts;
|
|
| 102 | + |
|
| 103 | + public TorSettings() {
|
|
| 104 | + }
|
|
| 105 | + |
|
| 106 | + public TorSettings(GeckoBundle bundle) {
|
|
| 107 | + try {
|
|
| 108 | + GeckoBundle qs = bundle.getBundle("quickstart");
|
|
| 109 | + GeckoBundle bridges = bundle.getBundle("bridges");
|
|
| 110 | + GeckoBundle proxy = bundle.getBundle("proxy");
|
|
| 111 | + GeckoBundle firewall = bundle.getBundle("firewall");
|
|
| 112 | + |
|
| 113 | + bridgesEnabled = bridges.getBoolean("enabled");
|
|
| 114 | + bridgesSource = BridgeSource.fromInt(bridges.getInt("source"));
|
|
| 115 | + bridgesBuiltinType = bridges.getString("builtin_type");
|
|
| 116 | + bridgeBridgeStrings = bridges.getStringArray("bridge_strings");
|
|
| 117 | + |
|
| 118 | + quickstart = qs.getBoolean("enabled");
|
|
| 119 | + |
|
| 120 | + firewallEnabled = firewall.getBoolean("enabled");
|
|
| 121 | + firewallAllowedPorts = firewall.getIntArray("allowed_ports");
|
|
| 122 | + |
|
| 123 | + proxyEnabled = proxy.getBoolean("enabled");
|
|
| 124 | + proxyAddress = proxy.getString("address");
|
|
| 125 | + proxyUsername = proxy.getString("username");
|
|
| 126 | + proxyPassword = proxy.getString("password");
|
|
| 127 | + proxyPort = proxy.getInt("port");
|
|
| 128 | + proxyType = ProxyType.fromInt(proxy.getInt("type"));
|
|
| 129 | + |
|
| 130 | + loaded = true;
|
|
| 131 | + } catch (Exception e) {
|
|
| 132 | + Log.e("TorSettings", "bundle access error: " + e.toString(), e);
|
|
| 133 | + }
|
|
| 134 | + }
|
|
| 135 | + |
|
| 136 | + public GeckoBundle asGeckoBundle() {
|
|
| 137 | + GeckoBundle bundle = new GeckoBundle();
|
|
| 138 | + |
|
| 139 | + GeckoBundle qs = new GeckoBundle();
|
|
| 140 | + GeckoBundle bridges = new GeckoBundle();
|
|
| 141 | + GeckoBundle proxy = new GeckoBundle();
|
|
| 142 | + GeckoBundle firewall = new GeckoBundle();
|
|
| 143 | + |
|
| 144 | + bridges.putBoolean("enabled", bridgesEnabled);
|
|
| 145 | + bridges.putInt("source", bridgesSource.toInt());
|
|
| 146 | + bridges.putString("builtin_type", bridgesBuiltinType);
|
|
| 147 | + bridges.putStringArray("bridge_strings", bridgeBridgeStrings);
|
|
| 148 | + |
|
| 149 | + qs.putBoolean("enabled", quickstart);
|
|
| 150 | + |
|
| 151 | + firewall.putBoolean("enabled", firewallEnabled);
|
|
| 152 | + firewall.putIntArray("allowed_ports", firewallAllowedPorts);
|
|
| 153 | + |
|
| 154 | + proxy.putBoolean("enabled", proxyEnabled);
|
|
| 155 | + proxy.putString("address", proxyAddress);
|
|
| 156 | + proxy.putString("username", proxyUsername);
|
|
| 157 | + proxy.putString("password", proxyPassword);
|
|
| 158 | + proxy.putInt("port", proxyPort);
|
|
| 159 | + proxy.putInt("type", proxyType.toInt());
|
|
| 160 | + |
|
| 161 | + bundle.putBundle("quickstart", qs);
|
|
| 162 | + bundle.putBundle("bridges", bridges);
|
|
| 163 | + bundle.putBundle("proxy", proxy);
|
|
| 164 | + bundle.putBundle("firewall", firewall);
|
|
| 165 | + |
|
| 166 | + return bundle;
|
|
| 167 | + }
|
|
| 168 | + |
|
| 169 | + public boolean isLoaded() {
|
|
| 170 | + return this.loaded;
|
|
| 171 | + }
|
|
| 172 | +} |
| 1 | +package org.mozilla.geckoview.androidlegacysettings;
|
|
| 2 | + |
|
| 3 | +import android.content.Context;
|
|
| 4 | +import android.content.SharedPreferences;
|
|
| 5 | +import org.mozilla.gecko.GeckoAppShell;
|
|
| 6 | + |
|
| 7 | +import java.util.Locale;
|
|
| 8 | + |
|
| 9 | +// tor-android-service utils/Prefs.java
|
|
| 10 | + |
|
| 11 | +/* package */ class Prefs {
|
|
| 12 | + private final static String PREF_BRIDGES_ENABLED = "pref_bridges_enabled";
|
|
| 13 | + private final static String PREF_BRIDGES_LIST = "pref_bridges_list";
|
|
| 14 | + |
|
| 15 | + private static SharedPreferences prefs;
|
|
| 16 | + |
|
| 17 | + // OrbotConstants
|
|
| 18 | + private final static String PREF_TOR_SHARED_PREFS = "org.torproject.android_preferences";
|
|
| 19 | + |
|
| 20 | + |
|
| 21 | + // tor-android-service utils/TorServiceUtil.java
|
|
| 22 | + |
|
| 23 | + private static void setContext() {
|
|
| 24 | + if (prefs == null) {
|
|
| 25 | + prefs = GeckoAppShell.getApplicationContext().getSharedPreferences(PREF_TOR_SHARED_PREFS,
|
|
| 26 | + Context.MODE_MULTI_PROCESS);
|
|
| 27 | + }
|
|
| 28 | + }
|
|
| 29 | + |
|
| 30 | + public static boolean getBoolean(String key, boolean def) {
|
|
| 31 | + setContext();
|
|
| 32 | + return prefs.getBoolean(key, def);
|
|
| 33 | + }
|
|
| 34 | + |
|
| 35 | + public static void putBoolean(String key, boolean value) {
|
|
| 36 | + setContext();
|
|
| 37 | + prefs.edit().putBoolean(key, value).apply();
|
|
| 38 | + }
|
|
| 39 | + |
|
| 40 | + public static void putString(String key, String value) {
|
|
| 41 | + setContext();
|
|
| 42 | + prefs.edit().putString(key, value).apply();
|
|
| 43 | + }
|
|
| 44 | + |
|
| 45 | + public static String getString(String key, String def) {
|
|
| 46 | + setContext();
|
|
| 47 | + return prefs.getString(key, def);
|
|
| 48 | + }
|
|
| 49 | + |
|
| 50 | + public static boolean bridgesEnabled() {
|
|
| 51 | + setContext();
|
|
| 52 | + return prefs.getBoolean(PREF_BRIDGES_ENABLED, false);
|
|
| 53 | + }
|
|
| 54 | + |
|
| 55 | + public static String getBridgesList() {
|
|
| 56 | + setContext();
|
|
| 57 | + // was "meek" for (Locale.getDefault().getLanguage().equals("fa")) and "obfs4" for the rest from a 2019 commit
|
|
| 58 | + // but that has stopped representing a good default sometime since so not importing for new users
|
|
| 59 | + String list = prefs.getString(PREF_BRIDGES_LIST, "");
|
|
| 60 | + return list;
|
|
| 61 | + }
|
|
| 62 | + |
|
| 63 | + |
|
| 64 | +} |
| 1 | +package org.mozilla.geckoview.androidlegacysettings;
|
|
| 2 | + |
|
| 3 | +import java.io.IOException;
|
|
| 4 | + |
|
| 5 | +import android.content.SharedPreferences;
|
|
| 6 | + |
|
| 7 | +import org.mozilla.gecko.GeckoAppShell;
|
|
| 8 | + |
|
| 9 | +import org.mozilla.geckoview.TorSettings;
|
|
| 10 | + |
|
| 11 | +public class TorLegacyAndroidSettings {
|
|
| 12 | + |
|
| 13 | + private static String PREF_USE_MOZ_PREFS = "tor_use_moz_prefs";
|
|
| 14 | + |
|
| 15 | + public static boolean unmigrated() {
|
|
| 16 | + return !Prefs.getBoolean(PREF_USE_MOZ_PREFS, false);
|
|
| 17 | + }
|
|
| 18 | + |
|
| 19 | + public static void setUnmigrated() {
|
|
| 20 | + Prefs.putBoolean(PREF_USE_MOZ_PREFS, false);
|
|
| 21 | + }
|
|
| 22 | + |
|
| 23 | + public static void setMigrated() {
|
|
| 24 | + Prefs.putBoolean(PREF_USE_MOZ_PREFS, true);
|
|
| 25 | + }
|
|
| 26 | + |
|
| 27 | + public static TorSettings loadTorSettings() {
|
|
| 28 | + TorSettings settings = new TorSettings();
|
|
| 29 | + |
|
| 30 | + // always true, tor is enabled in TB
|
|
| 31 | + settings.enabled = true;
|
|
| 32 | + |
|
| 33 | + // firefox-android disconnected quick start a while ago so it's untracked
|
|
| 34 | + settings.quickstart = false;
|
|
| 35 | + |
|
| 36 | + settings.bridgesEnabled = Prefs.bridgesEnabled();
|
|
| 37 | + |
|
| 38 | + // tor-android-service CustomTorInstaller.java
|
|
| 39 | +/*
|
|
| 40 | + BridgesList is an overloaded field, which can cause some confusion.
|
|
| 41 | + The list can be:
|
|
| 42 | + 1) a filter like obfs4, meek, or snowflake OR
|
|
| 43 | + 2) it can be a custom bridge
|
|
| 44 | + For (1), we just pass back all bridges, the filter will occur
|
|
| 45 | + elsewhere in the library.
|
|
| 46 | + For (2) we return the bridge list as a raw stream.
|
|
| 47 | + If length is greater than 9, then we know this is a custom bridge
|
|
| 48 | + */
|
|
| 49 | + String userDefinedBridgeList = Prefs.getBridgesList();
|
|
| 50 | + boolean userDefinedBridge = userDefinedBridgeList.length() > 9;
|
|
| 51 | + // Terrible hack. Must keep in sync with topl::addBridgesFromResources.
|
|
| 52 | + if (!userDefinedBridge) {
|
|
| 53 | + settings.bridgesSource = TorSettings.BridgeSource.BuiltIn;
|
|
| 54 | + switch (userDefinedBridgeList) {
|
|
| 55 | + case "obfs4":
|
|
| 56 | + settings.bridgesBuiltinType = "objs4";
|
|
| 57 | + break;
|
|
| 58 | + case "meek":
|
|
| 59 | + settings.bridgesBuiltinType = "meek_azure";
|
|
| 60 | + break;
|
|
| 61 | + case "snowflake":
|
|
| 62 | + settings.bridgesBuiltinType = "snowflake";
|
|
| 63 | + break;
|
|
| 64 | + default:
|
|
| 65 | + settings.bridgesSource = TorSettings.BridgeSource.Invalid;
|
|
| 66 | + break;
|
|
| 67 | + }
|
|
| 68 | + } else {
|
|
| 69 | + settings.bridgesSource = TorSettings.BridgeSource.UserProvided; // user provided
|
|
| 70 | + settings.bridgeBridgeStrings = userDefinedBridgeList.split("\r\n");
|
|
| 71 | + }
|
|
| 72 | + |
|
| 73 | + // Tor Browser Android doesn't take proxy and firewall settings
|
|
| 74 | + settings.proxyEnabled = false;
|
|
| 75 | + |
|
| 76 | + settings.firewallEnabled = false;
|
|
| 77 | + settings.firewallAllowedPorts = new int[0];
|
|
| 78 | + |
|
| 79 | + return settings;
|
|
| 80 | + }
|
|
| 81 | +}
|
|
| 82 | + |
|
| 83 | + |
|
| 84 | + |
| ... | ... | @@ -8,6 +8,8 @@ const lazy = {}; |
| 8 | 8 | ChromeUtils.defineESModuleGetters(lazy, {
|
| 9 | 9 | EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
|
| 10 | 10 | TorConnect: "resource://gre/modules/TorConnect.sys.mjs",
|
| 11 | + TorConnectTopics: "resource://gre/modules/TorConnect.sys.mjs",
|
|
| 12 | + TorSettingsTopics: "resource://gre/modules/TorSettings.sys.mjs",
|
|
| 11 | 13 | TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
| 12 | 14 | TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
|
| 13 | 15 | });
|
| ... | ... | @@ -23,11 +25,22 @@ const logger = new ConsoleAPI({ |
| 23 | 25 | prefix: "TorAndroidIntegration",
|
| 24 | 26 | });
|
| 25 | 27 | |
| 28 | +const EmittedEvents = Object.freeze( {
|
|
| 29 | + settingsReady: "GeckoView:Tor:SettingsReady",
|
|
| 30 | + settingsChanged: "GeckoView:Tor:SettingsChanged",
|
|
| 31 | +});
|
|
| 32 | + |
|
| 26 | 33 | const ListenedEvents = Object.freeze({
|
| 27 | 34 | settingsGet: "GeckoView:Tor:SettingsGet",
|
| 35 | + // The data is passed directly to TorSettings.
|
|
| 28 | 36 | settingsSet: "GeckoView:Tor:SettingsSet",
|
| 29 | 37 | settingsApply: "GeckoView:Tor:SettingsApply",
|
| 30 | 38 | settingsSave: "GeckoView:Tor:SettingsSave",
|
| 39 | + bootstrapBegin: "GeckoView:Tor:BootstrapBegin",
|
|
| 40 | + // Optionally takes a countryCode, as data.countryCode.
|
|
| 41 | + bootstrapBeginAuto: "GeckoView:Tor:BootstrapBeginAuto",
|
|
| 42 | + bootstrapCancel: "GeckoView:Tor:BootstrapCancel",
|
|
| 43 | + bootstrapGetState: "GeckoView:Tor:BootstrapGetState",
|
|
| 31 | 44 | });
|
| 32 | 45 | |
| 33 | 46 | class TorAndroidIntegrationImpl {
|
| ... | ... | @@ -41,6 +54,14 @@ class TorAndroidIntegrationImpl { |
| 41 | 54 | |
| 42 | 55 | this.#bootstrapMethodReset();
|
| 43 | 56 | Services.prefs.addObserver(Prefs.useNewBootstrap, this);
|
| 57 | + |
|
| 58 | + for (const topic in lazy.TorConnectTopics) {
|
|
| 59 | + Services.obs.addObserver(this, lazy.TorConnectTopics[topic]);
|
|
| 60 | + }
|
|
| 61 | + |
|
| 62 | + for (const topic in lazy.TorSettingsTopics) {
|
|
| 63 | + Services.obs.addObserver(this, lazy.TorSettingsTopics[topic]);
|
|
| 64 | + }
|
|
| 44 | 65 | }
|
| 45 | 66 | |
| 46 | 67 | async #initNewBootstrap() {
|
| ... | ... | @@ -67,6 +88,14 @@ class TorAndroidIntegrationImpl { |
| 67 | 88 | this.#bootstrapMethodReset();
|
| 68 | 89 | }
|
| 69 | 90 | break;
|
| 91 | + case lazy.TorConnectTopics.StateChange:
|
|
| 92 | + break;
|
|
| 93 | + case lazy.TorSettingsTopics.Ready:
|
|
| 94 | + lazy.EventDispatcher.instance.sendRequest({
|
|
| 95 | + type: EmittedEvents.settingsReady,
|
|
| 96 | + settings: lazy.TorSettings.getSettings(),
|
|
| 97 | + });
|
|
| 98 | + break;
|
|
| 70 | 99 | }
|
| 71 | 100 | }
|
| 72 | 101 | |
| ... | ... | @@ -74,24 +103,36 @@ class TorAndroidIntegrationImpl { |
| 74 | 103 | logger.debug(`Received event ${event}`, data);
|
| 75 | 104 | try {
|
| 76 | 105 | switch (event) {
|
| 77 | - case settingsGet:
|
|
| 106 | + case ListenedEvents.settingsGet:
|
|
| 78 | 107 | callback?.onSuccess(lazy.TorSettings.getSettings());
|
| 79 | 108 | return;
|
| 80 | - case settingsSet:
|
|
| 109 | + case ListenedEvents.settingsSet:
|
|
| 81 | 110 | // This does not throw, so we do not have any way to report the error!
|
| 82 | 111 | lazy.TorSettings.setSettings(data);
|
| 83 | 112 | break;
|
| 84 | - case settingsApply:
|
|
| 113 | + case ListenedEvents.settingsApply:
|
|
| 85 | 114 | await lazy.TorSettings.applySettings();
|
| 86 | 115 | break;
|
| 87 | - case settingsSave:
|
|
| 116 | + case ListenedEvents.settingsSave:
|
|
| 88 | 117 | await lazy.TorSettings.saveSettings();
|
| 89 | 118 | break;
|
| 119 | + case ListenedEvents.bootstrapBegin:
|
|
| 120 | + lazy.TorConnect.beginBootstrap();
|
|
| 121 | + break;
|
|
| 122 | + case ListenedEvents.bootstrapBeginAuto:
|
|
| 123 | + lazy.TorConnect.beginAutoBootstrap(data.countryCode);
|
|
| 124 | + break;
|
|
| 125 | + case ListenedEvents.bootstrapCancel:
|
|
| 126 | + lazy.TorConnect.cancelBootstrap();
|
|
| 127 | + break;
|
|
| 128 | + case ListenedEvents.bootstrapGetState:
|
|
| 129 | + callback?.onSuccess(lazy.TorConnect.state);
|
|
| 130 | + return;
|
|
| 90 | 131 | }
|
| 91 | 132 | callback?.onSuccess();
|
| 92 | 133 | } catch (e) {
|
| 93 | - logger.error();
|
|
| 94 | - callback?.sendError(e);
|
|
| 134 | + logger.error(`Error while handling event ${event}`, e);
|
|
| 135 | + callback?.onError(e);
|
|
| 95 | 136 | }
|
| 96 | 137 | }
|
| 97 | 138 |