From 61e7d2ebf922ef36a337d9fff8a0b8acd7e34ee8 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 28 Mar 2023 15:39:47 +0100 Subject: [PATCH] Move Tor wrapper to library. --- .gitmodules | 3 + .idea/runConfigurations/BridgeTest.xml | 28 - bramble-android/build.gradle | 1 + .../bramble/BrambleAndroidModule.java | 2 +- .../plugin/tor/AndroidTorPluginFactory.java | 6 +- .../plugin/tor/wrapper/AndroidTorWrapper.java | 253 ------- bramble-core/build.gradle | 1 + .../bramble/plugin/tor/TorPlugin.java | 16 +- .../bramble/plugin/tor/TorPluginFactory.java | 2 +- .../tor/wrapper/AbstractTorWrapper.java | 707 ------------------ .../tor/wrapper/CircumventionProvider.java | 78 -- .../wrapper/CircumventionProviderImpl.java | 129 ---- .../bramble/plugin/tor/wrapper/TorUtils.java | 66 -- .../plugin/tor/wrapper/TorWrapper.java | 162 ---- .../CircumventionModule.java | 2 +- bramble-core/src/main/resources/bridges | 35 - .../src/main/resources/snowflake-params | 4 - .../CircumventionProviderImplTest.java | 92 --- bramble-java/build.gradle | 8 +- .../bramble/BrambleJavaModule.java | 2 +- .../plugin/tor/UnixTorPluginFactory.java | 6 +- .../plugin/tor/WindowsTorPluginFactory.java | 6 +- .../plugin/tor/wrapper/JavaTorWrapper.java | 47 -- .../plugin/tor/wrapper/UnixTorWrapper.java | 53 -- .../plugin/tor/wrapper/WindowsTorWrapper.java | 95 --- .../bramble/plugin/tor/BridgeTest.java | 288 ------- .../plugin/tor/TestPluginCallback.java | 57 -- .../BrambleJavaIntegrationTestComponent.java | 29 - .../bramble/test/TestResources.java | 40 - .../bramble/test/TestTorPortsModule.java | 26 - bramble-java/witness.gradle | 5 - .../briar/android/AndroidComponent.java | 2 +- .../android/settings/SettingsViewModel.java | 2 +- .../android/settings/TorSummaryProvider.java | 2 +- briar-headless/build.gradle | 2 + onionwrapper | 1 + settings.gradle | 6 + 37 files changed, 39 insertions(+), 2225 deletions(-) delete mode 100644 .idea/runConfigurations/BridgeTest.xml delete mode 100644 bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/AndroidTorWrapper.java delete mode 100644 bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/AbstractTorWrapper.java delete mode 100644 bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProvider.java delete mode 100644 bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProviderImpl.java delete mode 100644 bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/TorUtils.java delete mode 100644 bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/TorWrapper.java rename bramble-core/src/main/java/org/briarproject/{bramble/plugin/tor/wrapper => onionwrapper}/CircumventionModule.java (83%) delete mode 100644 bramble-core/src/main/resources/bridges delete mode 100644 bramble-core/src/main/resources/snowflake-params delete mode 100644 bramble-core/src/test/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProviderImplTest.java delete mode 100644 bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/JavaTorWrapper.java delete mode 100644 bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/UnixTorWrapper.java delete mode 100644 bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/WindowsTorWrapper.java delete mode 100644 bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java delete mode 100644 bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/TestPluginCallback.java delete mode 100644 bramble-java/src/test/java/org/briarproject/bramble/test/BrambleJavaIntegrationTestComponent.java delete mode 100644 bramble-java/src/test/java/org/briarproject/bramble/test/TestResources.java delete mode 100644 bramble-java/src/test/java/org/briarproject/bramble/test/TestTorPortsModule.java create mode 160000 onionwrapper diff --git a/.gitmodules b/.gitmodules index 10aa12786..664d536c8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "briar-mailbox"] path = briar-mailbox url = https://code.briarproject.org/briar/briar-mailbox.git +[submodule "onionwrapper"] + path = onionwrapper + url = https://code.briarproject.org/briar/onionwrapper.git diff --git a/.idea/runConfigurations/BridgeTest.xml b/.idea/runConfigurations/BridgeTest.xml deleted file mode 100644 index 5fe4c5a46..000000000 --- a/.idea/runConfigurations/BridgeTest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - false - true - false - - - \ No newline at end of file diff --git a/bramble-android/build.gradle b/bramble-android/build.gradle index 4a3083747..ab24ea57b 100644 --- a/bramble-android/build.gradle +++ b/bramble-android/build.gradle @@ -49,6 +49,7 @@ dependencies { implementation project(':bramble-api') implementation project(':bramble-core') + implementation project(':onionwrapper-android') implementation 'androidx.annotation:annotation:1.5.0' diff --git a/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java index f38219880..8c2e07e3f 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/BrambleAndroidModule.java @@ -4,13 +4,13 @@ import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockModule; import org.briarproject.bramble.battery.AndroidBatteryModule; import org.briarproject.bramble.io.DnsModule; import org.briarproject.bramble.network.AndroidNetworkModule; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionModule; import org.briarproject.bramble.reporting.ReportingModule; import org.briarproject.bramble.socks.SocksModule; import org.briarproject.bramble.system.AndroidSystemModule; import org.briarproject.bramble.system.AndroidTaskSchedulerModule; import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule; import org.briarproject.bramble.system.DefaultThreadFactoryModule; +import org.briarproject.onionwrapper.CircumventionModule; import dagger.Module; diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPluginFactory.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPluginFactory.java index 76cbfd397..2f39fb229 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPluginFactory.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPluginFactory.java @@ -18,10 +18,10 @@ import org.briarproject.bramble.api.plugin.TorSocksPort; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.WakefulIoExecutor; -import org.briarproject.bramble.plugin.tor.wrapper.AndroidTorWrapper; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider; -import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper; import org.briarproject.nullsafety.NotNullByDefault; +import org.briarproject.onionwrapper.AndroidTorWrapper; +import org.briarproject.onionwrapper.CircumventionProvider; +import org.briarproject.onionwrapper.TorWrapper; import java.io.File; import java.util.concurrent.Executor; diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/AndroidTorWrapper.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/AndroidTorWrapper.java deleted file mode 100644 index 3788ce923..000000000 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/AndroidTorWrapper.java +++ /dev/null @@ -1,253 +0,0 @@ -package org.briarproject.bramble.plugin.tor.wrapper; - -import android.app.Application; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; -import android.os.Build; - -import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLock; -import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager; -import org.briarproject.nullsafety.NotNullByDefault; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.logging.Logger; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import static android.os.Build.VERSION.SDK_INT; -import static java.util.Arrays.asList; -import static java.util.logging.Level.INFO; -import static java.util.logging.Logger.getLogger; - -/** - * A Tor wrapper for the Android operating system. - */ -@NotNullByDefault -public class AndroidTorWrapper extends AbstractTorWrapper { - - private static final List LIBRARY_ARCHITECTURES = - asList("armeabi-v7a", "arm64-v8a", "x86", "x86_64"); - - private static final String TOR_LIB_NAME = "libtor.so"; - private static final String OBFS4_LIB_NAME = "libobfs4proxy.so"; - private static final String SNOWFLAKE_LIB_NAME = "libsnowflake.so"; - - private static final Logger LOG = - getLogger(AndroidTorWrapper.class.getName()); - - private final Application app; - private final AndroidWakeLock wakeLock; - private final File torLib, obfs4Lib, snowflakeLib; - - /** - * @param app The application instance. - * @param wakeLockManager The interface for managing a shared wake lock. - * @param ioExecutor The wrapper will use this executor to run IO tasks, - * some of which may run for the lifetime of the wrapper, so the executor - * should have an unlimited thread pool. - * @param eventExecutor The wrapper will use this executor to call the - * {@link Observer observer} (if any). To ensure that events are observed - * in the order they occur, this executor should have a single thread (eg - * the app's main thread). - * @param architecture The processor architecture of the Tor and pluggable - * transport binaries. - * @param torDirectory The directory where the Tor process should keep its - * state. - * @param torSocksPort The port number to use for Tor's SOCKS port. - * @param torControlPort The port number to use for Tor's control port. - */ - public AndroidTorWrapper(Application app, - AndroidWakeLockManager wakeLockManager, - Executor ioExecutor, - Executor eventExecutor, - String architecture, - File torDirectory, - int torSocksPort, - int torControlPort) { - super(ioExecutor, eventExecutor, architecture, torDirectory, - torSocksPort, torControlPort); - this.app = app; - wakeLock = wakeLockManager.createWakeLock("TorPlugin"); - String nativeLibDir = app.getApplicationInfo().nativeLibraryDir; - torLib = new File(nativeLibDir, TOR_LIB_NAME); - obfs4Lib = new File(nativeLibDir, OBFS4_LIB_NAME); - snowflakeLib = new File(nativeLibDir, SNOWFLAKE_LIB_NAME); - } - - @Override - protected int getProcessId() { - return android.os.Process.myPid(); - } - - @Override - protected long getLastUpdateTime() { - try { - PackageManager pm = app.getPackageManager(); - PackageInfo pi = pm.getPackageInfo(app.getPackageName(), 0); - return pi.lastUpdateTime; - } catch (NameNotFoundException e) { - throw new AssertionError(e); - } - } - - @Override - public InputStream getResourceInputStream(String name, String extension) { - Resources res = app.getResources(); - // Extension is ignored on Android, resources are retrieved without it - int resId = res.getIdentifier(name, "raw", app.getPackageName()); - return res.openRawResource(resId); - } - - @Override - public void enableNetwork(boolean enable) throws IOException { - if (enable) wakeLock.acquire(); - try { - super.enableNetwork(enable); - } finally { - if (!enable) wakeLock.release(); - } - } - - @Override - public void stop() throws IOException { - try { - super.stop(); - } finally { - wakeLock.release(); - } - } - - @Override - protected File getTorExecutableFile() { - return torLib.exists() ? torLib : super.getTorExecutableFile(); - } - - @Override - protected File getObfs4ExecutableFile() { - return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile(); - } - - @Override - protected File getSnowflakeExecutableFile() { - return snowflakeLib.exists() ? snowflakeLib - : super.getSnowflakeExecutableFile(); - } - - @Override - protected void installTorExecutable() throws IOException { - installExecutable(super.getTorExecutableFile(), torLib, TOR_LIB_NAME); - } - - @Override - protected void installObfs4Executable() throws IOException { - installExecutable(super.getObfs4ExecutableFile(), obfs4Lib, - OBFS4_LIB_NAME); - } - - @Override - protected void installSnowflakeExecutable() throws IOException { - installExecutable(super.getSnowflakeExecutableFile(), snowflakeLib, - SNOWFLAKE_LIB_NAME); - } - - private void installExecutable(File extracted, File lib, String libName) - throws IOException { - if (lib.exists()) { - // If an older version left behind a binary, delete it - if (extracted.exists()) { - if (extracted.delete()) LOG.info("Deleted old binary"); - else LOG.info("Failed to delete old binary"); - } - } else if (SDK_INT < 29) { - // The binary wasn't extracted at install time. Try to extract it - extractLibraryFromApk(libName, extracted); - } else { - // No point extracting the binary, we won't be allowed to execute it - throw new FileNotFoundException(lib.getAbsolutePath()); - } - } - - private void extractLibraryFromApk(String libName, File dest) - throws IOException { - File sourceDir = new File(app.getApplicationInfo().sourceDir); - if (sourceDir.isFile()) { - // Look for other APK files in the same directory, if we're allowed - File parent = sourceDir.getParentFile(); - if (parent != null) sourceDir = parent; - } - List libPaths = getSupportedLibraryPaths(libName); - for (File apk : findApkFiles(sourceDir)) { - @SuppressWarnings("IOStreamConstructor") - ZipInputStream zin = new ZipInputStream(new FileInputStream(apk)); - for (ZipEntry e = zin.getNextEntry(); e != null; - e = zin.getNextEntry()) { - if (libPaths.contains(e.getName())) { - if (LOG.isLoggable(INFO)) { - LOG.info("Extracting " + e.getName() - + " from " + apk.getAbsolutePath()); - } - extract(zin, dest); // Zip input stream will be closed - return; - } - } - zin.close(); - } - throw new FileNotFoundException(libName); - } - - /** - * Returns all files with the extension .apk or .APK under the given root. - */ - private List findApkFiles(File root) { - List files = new ArrayList<>(); - findApkFiles(root, files); - return files; - } - - private void findApkFiles(File f, List files) { - if (f.isFile() && f.getName().toLowerCase().endsWith(".apk")) { - files.add(f); - } else if (f.isDirectory()) { - File[] children = f.listFiles(); - if (children != null) { - for (File child : children) findApkFiles(child, files); - } - } - } - - /** - * Returns the paths at which libraries with the given name would be found - * inside an APK file, for all architectures supported by the device, in - * order of preference. - */ - private List getSupportedLibraryPaths(String libName) { - List architectures = new ArrayList<>(); - for (String abi : getSupportedArchitectures()) { - if (LIBRARY_ARCHITECTURES.contains(abi)) { - architectures.add("lib/" + abi + "/" + libName); - } - } - return architectures; - } - - private Collection getSupportedArchitectures() { - List abis = new ArrayList<>(); - if (SDK_INT >= 21) { - abis.addAll(asList(Build.SUPPORTED_ABIS)); - } else { - abis.add(Build.CPU_ABI); - if (Build.CPU_ABI2 != null) abis.add(Build.CPU_ABI2); - } - return abis; - } -} diff --git a/bramble-core/build.gradle b/bramble-core/build.gradle index a067dd390..cee914634 100644 --- a/bramble-core/build.gradle +++ b/bramble-core/build.gradle @@ -10,6 +10,7 @@ apply from: '../dagger.gradle' dependencies { api project(':bramble-api') + api project(':onionwrapper-core') api 'org.briarproject:jtorctl:0.5' diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index 1c6db7698..69239bc9a 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -25,14 +25,14 @@ import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent; import org.briarproject.bramble.api.system.LocationUtils; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType; -import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper; -import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.HiddenServiceProperties; -import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.Observer; -import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.TorState; import org.briarproject.nullsafety.InterfaceNotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault; +import org.briarproject.onionwrapper.CircumventionProvider; +import org.briarproject.onionwrapper.CircumventionProvider.BridgeType; +import org.briarproject.onionwrapper.TorWrapper; +import org.briarproject.onionwrapper.TorWrapper.HiddenServiceProperties; +import org.briarproject.onionwrapper.TorWrapper.Observer; +import org.briarproject.onionwrapper.TorWrapper.TorState; import java.io.IOException; import java.net.InetSocketAddress; @@ -78,12 +78,12 @@ import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.MEEK; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.SNOWFLAKE; import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; +import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.MEEK; +import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.SNOWFLAKE; @InterfaceNotNullByDefault class TorPlugin implements DuplexPlugin, EventListener { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java index ce5a902ab..b04082d03 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPluginFactory.java @@ -19,8 +19,8 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.WakefulIoExecutor; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider; import org.briarproject.nullsafety.NotNullByDefault; +import org.briarproject.onionwrapper.CircumventionProvider; import java.io.File; import java.util.concurrent.Executor; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/AbstractTorWrapper.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/AbstractTorWrapper.java deleted file mode 100644 index 69c7b43de..000000000 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/AbstractTorWrapper.java +++ /dev/null @@ -1,707 +0,0 @@ -package org.briarproject.bramble.plugin.tor.wrapper; - -import net.freehaven.tor.control.EventHandler; -import net.freehaven.tor.control.TorControlConnection; - -import org.briarproject.nullsafety.InterfaceNotNullByDefault; -import org.briarproject.nullsafety.NotNullByDefault; - -import java.io.ByteArrayInputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Scanner; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.GuardedBy; -import javax.annotation.concurrent.ThreadSafe; - -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; -import static java.util.logging.Level.INFO; -import static java.util.logging.Level.WARNING; -import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; -import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY; -import static org.briarproject.bramble.plugin.tor.wrapper.TorUtils.UTF_8; -import static org.briarproject.bramble.plugin.tor.wrapper.TorUtils.copyAndClose; -import static org.briarproject.bramble.plugin.tor.wrapper.TorUtils.scrubOnion; -import static org.briarproject.bramble.plugin.tor.wrapper.TorUtils.tryToClose; -import static org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.TorState.CONNECTED; -import static org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.TorState.CONNECTING; -import static org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.TorState.DISABLED; -import static org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.TorState.STARTING_STOPPING; -import static org.briarproject.nullsafety.NullSafety.requireNonNull; - -@InterfaceNotNullByDefault -abstract class AbstractTorWrapper implements EventHandler, TorWrapper { - - private static final String[] EVENTS = { - "CIRC", - "ORCONN", - "STATUS_GENERAL", - "STATUS_CLIENT", - "HS_DESC", - "NOTICE", - "WARN", - "ERR" - }; - - private static final String OWNER = "__OwningControllerProcess"; - private static final int COOKIE_TIMEOUT_MS = 3000; - private static final int COOKIE_POLLING_INTERVAL_MS = 200; - private static final Pattern BOOTSTRAP_PERCENTAGE = - Pattern.compile(".*PROGRESS=(\\d{1,3}).*"); - - protected final Executor ioExecutor; - protected final Executor eventExecutor; - private final String architecture; - private final File torDirectory, configFile, doneFile, cookieFile; - private final int torSocksPort; - private final int torControlPort; - private final AtomicBoolean used = new AtomicBoolean(false); - - protected final NetworkState state = new NetworkState(); - - private volatile Socket controlSocket = null; - private volatile TorControlConnection controlConnection = null; - - protected abstract int getProcessId(); - - protected abstract long getLastUpdateTime(); - - protected abstract InputStream getResourceInputStream(String name, - String extension); - - AbstractTorWrapper(Executor ioExecutor, - Executor eventExecutor, - String architecture, - File torDirectory, - int torSocksPort, - int torControlPort) { - this.ioExecutor = ioExecutor; - this.eventExecutor = eventExecutor; - this.architecture = architecture; - this.torDirectory = torDirectory; - this.torSocksPort = torSocksPort; - this.torControlPort = torControlPort; - configFile = new File(torDirectory, "torrc"); - doneFile = new File(torDirectory, "done"); - cookieFile = new File(torDirectory, ".tor/control_auth_cookie"); - } - - protected File getTorExecutableFile() { - return new File(torDirectory, "tor"); - } - - protected File getObfs4ExecutableFile() { - return new File(torDirectory, "obfs4proxy"); - } - - protected File getSnowflakeExecutableFile() { - return new File(torDirectory, "snowflake"); - } - - @Override - public void setObserver(@Nullable Observer observer) { - state.setObserver(observer); - } - - @Override - public void start() throws IOException, InterruptedException { - if (used.getAndSet(true)) throw new IllegalStateException(); - if (!torDirectory.exists()) { - if (!torDirectory.mkdirs()) { - throw new IOException("Could not create Tor directory"); - } - } - // Install or update the assets if necessary - if (!assetsAreUpToDate()) installAssets(); - // Start from the default config every time - extract(getConfigInputStream(), configFile); - if (cookieFile.exists() && !cookieFile.delete()) - LOG.warning("Old auth cookie not deleted"); - // Start a new Tor process - LOG.info("Starting Tor"); - File torFile = getTorExecutableFile(); - String torPath = torFile.getAbsolutePath(); - String configPath = configFile.getAbsolutePath(); - String pid = String.valueOf(getProcessId()); - Process torProcess; - ProcessBuilder pb = - new ProcessBuilder(torPath, "-f", configPath, OWNER, pid); - Map env = pb.environment(); - env.put("HOME", torDirectory.getAbsolutePath()); - pb.directory(torDirectory); - pb.redirectErrorStream(true); - try { - torProcess = pb.start(); - } catch (SecurityException e) { - throw new IOException(e); - } - // Wait for the Tor process to start - waitForTorToStart(torProcess); - // Wait for the auth cookie file to be created/updated - long start = System.currentTimeMillis(); - while (cookieFile.length() < 32) { - if (System.currentTimeMillis() - start > COOKIE_TIMEOUT_MS) { - throw new IOException("Auth cookie not created"); - } - //noinspection BusyWait - Thread.sleep(COOKIE_POLLING_INTERVAL_MS); - } - LOG.info("Auth cookie created"); - // Open a control connection and authenticate using the cookie file - controlSocket = new Socket("127.0.0.1", torControlPort); - controlConnection = new TorControlConnection(controlSocket); - controlConnection.authenticate(read(cookieFile)); - // Tell Tor to exit when the control connection is closed - controlConnection.takeOwnership(); - controlConnection.resetConf(singletonList(OWNER)); - // Register to receive events from the Tor process - controlConnection.setEventHandler(this); - controlConnection.setEvents(asList(EVENTS)); - // Check whether Tor has already bootstrapped - String info = controlConnection.getInfo("status/bootstrap-phase"); - if (info != null && info.contains("PROGRESS=")) { - int percentage = parseBootstrapPercentage(info); - if (percentage == 100) LOG.info("Tor has already bootstrapped"); - state.setBootstrapPercentage(percentage); - } - // Check whether Tor has already built a circuit - info = controlConnection.getInfo("status/circuit-established"); - if ("1".equals(info)) { - LOG.info("Tor has already built a circuit"); - state.setCircuitBuilt(true); - } - state.setStarted(); - } - - private boolean assetsAreUpToDate() { - return doneFile.lastModified() > getLastUpdateTime(); - } - - private void installAssets() throws IOException { - // The done file may already exist from a previous installation - //noinspection ResultOfMethodCallIgnored - doneFile.delete(); - installTorExecutable(); - installObfs4Executable(); - installSnowflakeExecutable(); - extract(getConfigInputStream(), configFile); - if (!doneFile.createNewFile()) { - LOG.warning("Failed to create done file"); - } - } - - protected void extract(InputStream in, File dest) throws IOException { - @SuppressWarnings("IOStreamConstructor") - OutputStream out = new FileOutputStream(dest); - copyAndClose(in, out); - } - - protected void installTorExecutable() throws IOException { - if (LOG.isLoggable(INFO)) { - LOG.info("Installing Tor binary for " + architecture); - } - File torFile = getTorExecutableFile(); - extract(getExecutableInputStream("tor"), torFile); - if (!torFile.setExecutable(true, true)) throw new IOException(); - } - - protected void installObfs4Executable() throws IOException { - if (LOG.isLoggable(INFO)) { - LOG.info("Installing obfs4proxy binary for " + architecture); - } - File obfs4File = getObfs4ExecutableFile(); - extract(getExecutableInputStream("obfs4proxy"), obfs4File); - if (!obfs4File.setExecutable(true, true)) throw new IOException(); - } - - protected void installSnowflakeExecutable() throws IOException { - if (LOG.isLoggable(INFO)) { - LOG.info("Installing snowflake binary for " + architecture); - } - File snowflakeFile = getSnowflakeExecutableFile(); - extract(getExecutableInputStream("snowflake"), snowflakeFile); - if (!snowflakeFile.setExecutable(true, true)) throw new IOException(); - } - - private InputStream getExecutableInputStream(String basename) { - String ext = getExecutableExtension(); - return requireNonNull( - getResourceInputStream(architecture + "/" + basename, ext)); - } - - protected String getExecutableExtension() { - return ""; - } - - private static void append(StringBuilder strb, String name, Object value) { - strb.append(name); - strb.append(" "); - strb.append(value); - strb.append("\n"); - } - - private InputStream getConfigInputStream() { - File dataDirectory = new File(torDirectory, ".tor"); - StringBuilder strb = new StringBuilder(); - append(strb, "ControlPort", torControlPort); - append(strb, "CookieAuthentication", 1); - append(strb, "DataDirectory", dataDirectory.getAbsolutePath()); - append(strb, "DisableNetwork", 1); - append(strb, "RunAsDaemon", 1); - append(strb, "SafeSocks", 1); - append(strb, "SocksPort", torSocksPort); - strb.append("GeoIPFile\n"); - strb.append("GeoIPv6File\n"); - append(strb, "ConnectionPadding", 0); - String obfs4Path = getObfs4ExecutableFile().getAbsolutePath(); - append(strb, "ClientTransportPlugin obfs4 exec", obfs4Path); - append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path); - String snowflakePath = getSnowflakeExecutableFile().getAbsolutePath(); - append(strb, "ClientTransportPlugin snowflake exec", snowflakePath); - return new ByteArrayInputStream(strb.toString().getBytes(UTF_8)); - } - - private byte[] read(File f) throws IOException { - byte[] b = new byte[(int) f.length()]; - FileInputStream in = new FileInputStream(f); - try { - int offset = 0; - while (offset < b.length) { - int read = in.read(b, offset, b.length - offset); - if (read == -1) throw new EOFException(); - offset += read; - } - return b; - } finally { - tryToClose(in, LOG, WARNING); - } - } - - protected void waitForTorToStart(Process torProcess) - throws InterruptedException, IOException { - Scanner stdout = new Scanner(torProcess.getInputStream()); - // Log the first line of stdout (contains Tor and library versions) - if (stdout.hasNextLine()) LOG.info(stdout.nextLine()); - // Read the process's stdout (and redirected stderr) until it detaches - while (stdout.hasNextLine()) stdout.nextLine(); - stdout.close(); - // Wait for the process to detach or exit - int exit = torProcess.waitFor(); - if (exit != 0) throw new IOException("Tor exited with value " + exit); - } - - @Override - public HiddenServiceProperties publishHiddenService(int localPort, - int remotePort, @Nullable String privKey) throws IOException { - Map portLines = - singletonMap(remotePort, "127.0.0.1:" + localPort); - // Use the control connection to set up the hidden service - Map response; - if (privKey == null) { - response = getControlConnection().addOnion("NEW:ED25519-V3", - portLines, null); - } else { - response = getControlConnection().addOnion(privKey, portLines); - } - if (!response.containsKey(HS_ADDRESS)) { - throw new IOException("Missing hidden service address"); - } - if (privKey == null && !response.containsKey(HS_PRIVKEY)) { - throw new IOException("Missing private key"); - } - String onion = response.get(HS_ADDRESS); - if (privKey == null) privKey = response.get(HS_PRIVKEY); - return new HiddenServiceProperties(onion, privKey); - } - - @Override - public void removeHiddenService(String onion) throws IOException { - getControlConnection().delOnion(onion); - } - - @Override - public void enableNetwork(boolean enable) throws IOException { - if (!state.enableNetwork(enable)) return; // Unchanged - getControlConnection().setConf("DisableNetwork", enable ? "0" : "1"); - } - - @Override - public void enableBridges(List bridges) throws IOException { - if (!state.setBridges(bridges)) return; // Unchanged - List conf = new ArrayList<>(bridges.size() + 1); - conf.add("UseBridges 1"); - conf.addAll(bridges); - getControlConnection().setConf(conf); - } - - @Override - public void disableBridges() throws IOException { - if (!state.setBridges(emptyList())) return; // Unchanged - getControlConnection().setConf("UseBridges", "0"); - } - - @Override - public void stop() throws IOException { - state.setStopped(); - if (controlSocket != null && controlConnection != null) { - LOG.info("Stopping Tor"); - try { - controlConnection.shutdownTor("TERM"); - } finally { - tryToClose(controlSocket, LOG, WARNING); - } - } - } - - @Override - public void circuitStatus(String status, String id, String path) { - // In case of races between receiving CIRCUIT_ESTABLISHED and setting - // DisableNetwork, set our circuitBuilt flag if not already set - if (status.equals("BUILT") && state.setCircuitBuilt(true)) { - LOG.info("Circuit built"); - } - } - - @Override - public void streamStatus(String status, String id, String target) { - } - - @Override - public void orConnStatus(String status, String orName) { - if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status); - - if (status.equals("CONNECTED")) state.onOrConnectionConnected(); - else if (status.equals("CLOSED")) state.onOrConnectionClosed(); - } - - @Override - public void bandwidthUsed(long read, long written) { - } - - @Override - public void newDescriptors(List orList) { - } - - @Override - public void message(String severity, String msg) { - if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); - } - - @Override - public void unrecognized(String type, String msg) { - if (type.equals("STATUS_CLIENT")) { - handleClientStatus(removeSeverity(msg)); - } else if (type.equals("STATUS_GENERAL")) { - handleGeneralStatus(removeSeverity(msg)); - } else if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) { - String[] parts = msg.split(" "); - if (parts.length < 2) { - LOG.warning("Failed to parse HS_DESC UPLOADED event"); - } else if (LOG.isLoggable(INFO)) { - String onion = parts[1]; - LOG.info("V3 descriptor uploaded for " + scrubOnion(onion)); - state.onHsDescriptorUploaded(onion); - } - } - } - - private String removeSeverity(String msg) { - return msg.replaceFirst("[^ ]+ ", ""); - } - - private void handleClientStatus(String msg) { - if (msg.startsWith("BOOTSTRAP PROGRESS=")) { - int percentage = parseBootstrapPercentage(msg); - if (percentage == 100) LOG.info("Bootstrapped"); - state.setBootstrapPercentage(percentage); - } else if (msg.startsWith("CIRCUIT_ESTABLISHED")) { - if (state.setCircuitBuilt(true)) LOG.info("Circuit built"); - } else if (msg.startsWith("CIRCUIT_NOT_ESTABLISHED")) { - if (state.setCircuitBuilt(false)) { - LOG.info("Circuit not built"); - // TODO: Disable and re-enable network to prompt Tor to rebuild - // its guard/bridge connections? This will also close any - // established circuits, which might still be functioning - } - } - } - - private int parseBootstrapPercentage(String s) { - Matcher matcher = BOOTSTRAP_PERCENTAGE.matcher(s); - if (matcher.matches()) { - try { - return Integer.parseInt(matcher.group(1)); - } catch (NumberFormatException e) { - // Fall through - } - } - if (LOG.isLoggable(WARNING)) { - LOG.warning("Failed to parse bootstrap percentage: " + s); - } - return 0; - } - - private void handleGeneralStatus(String msg) { - if (msg.startsWith("CLOCK_JUMPED")) { - Long time = parseLongArgument(msg, "TIME"); - if (time != null && LOG.isLoggable(WARNING)) { - LOG.warning("Clock jumped " + time + " seconds"); - } - } else if (msg.startsWith("CLOCK_SKEW")) { - Long skew = parseLongArgument(msg, "SKEW"); - if (skew != null && LOG.isLoggable(WARNING)) { - LOG.warning("Clock is skewed by " + skew + " seconds"); - } - } - } - - @Nullable - private Long parseLongArgument(String msg, String argName) { - String[] args = msg.split(" "); - for (String arg : args) { - if (arg.startsWith(argName + "=")) { - try { - return Long.parseLong(arg.substring(argName.length() + 1)); - } catch (NumberFormatException e) { - break; - } - } - } - if (LOG.isLoggable(WARNING)) { - LOG.warning("Failed to parse " + argName + " from '" + msg + "'"); - } - return null; - } - - @Override - public void controlConnectionClosed() { - if (state.isTorRunning()) { - // TODO: Restart the Tor process - LOG.warning("Control connection closed"); - } - } - - @Override - public void enableConnectionPadding(boolean enable) throws IOException { - if (!state.enableConnectionPadding(enable)) return; // Unchanged - getControlConnection().setConf("ConnectionPadding", enable ? "1" : "0"); - } - - @Override - public void enableIpv6(boolean enable) throws IOException { - if (!state.enableIpv6(enable)) return; // Unchanged - getControlConnection().setConf("ClientUseIPv4", enable ? "0" : "1"); - getControlConnection().setConf("ClientUseIPv6", enable ? "1" : "0"); - } - - @Override - public TorState getTorState() { - return state.getState(); - } - - @Override - public boolean isTorRunning() { - return state.isTorRunning(); - } - - private TorControlConnection getControlConnection() throws IOException { - TorControlConnection controlConnection = this.controlConnection; - if (controlConnection == null) { - throw new IOException("Control connection not opened"); - } - return controlConnection; - } - - @ThreadSafe - @NotNullByDefault - private class NetworkState { - - @GuardedBy("this") - @Nullable - private Observer observer = null; - - @GuardedBy("this") - private boolean started = false, - stopped = false, - networkInitialised = false, - networkEnabled = false, - paddingEnabled = false, - ipv6Enabled = false, - circuitBuilt = false; - - @GuardedBy("this") - private int bootstrapPercentage = 0; - - @GuardedBy("this") - private List bridges = emptyList(); - - @GuardedBy("this") - private int orConnectionsConnected = 0; - - @GuardedBy("this") - @Nullable - private TorState state = null; - - private synchronized void setObserver( - @Nullable Observer observer) { - this.observer = observer; - } - - @GuardedBy("this") - private void updateState() { - TorState newState = getState(); - if (newState != state) { - state = newState; - if (observer != null) { - // Notify the observer on the event executor - eventExecutor.execute(() -> observer.onState(newState)); - } - } - } - - private synchronized void setStarted() { - started = true; - updateState(); - } - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - private synchronized boolean isTorRunning() { - return started && !stopped; - } - - private synchronized void setStopped() { - stopped = true; - updateState(); - } - - private synchronized void setBootstrapPercentage(int percentage) { - if (percentage == bootstrapPercentage) return; - bootstrapPercentage = percentage; - if (observer != null) { - // Notify the observer on the event executor - eventExecutor.execute(() -> - observer.onBootstrapPercentage(percentage)); - } - updateState(); - } - - /** - * Sets the `circuitBuilt` flag and returns true if the flag has - * changed. - */ - private synchronized boolean setCircuitBuilt(boolean built) { - if (built == circuitBuilt) return false; // Unchanged - circuitBuilt = built; - updateState(); - return true; // Changed - } - - /** - * Sets the `networkEnabled` flag and returns true if the flag has - * changed. - */ - private synchronized boolean enableNetwork(boolean enable) { - boolean wasInitialised = networkInitialised; - boolean wasEnabled = networkEnabled; - networkInitialised = true; - networkEnabled = enable; - if (!enable) circuitBuilt = false; - if (!wasInitialised || enable != wasEnabled) { - updateState(); - } - return enable != wasEnabled; - } - - /** - * Sets the `paddingEnabled` flag and returns true if the flag has - * changed. Doesn't affect getState(). - */ - private synchronized boolean enableConnectionPadding(boolean enable) { - if (enable == paddingEnabled) return false; // Unchanged - paddingEnabled = enable; - return true; // Changed - } - - /** - * Sets the `ipv6Enabled` flag and returns true if the flag has - * changed. Doesn't affect getState(). - */ - private synchronized boolean enableIpv6(boolean enable) { - if (enable == ipv6Enabled) return false; // Unchanged - ipv6Enabled = enable; - return true; // Changed - } - - /** - * Sets the list of bridges being used and returns true if the - * list has changed. The list is empty if bridges are disabled. - * Doesn't affect getState(). - */ - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - private synchronized boolean setBridges(List bridges) { - if (this.bridges.equals(bridges)) return false; // Unchanged - this.bridges = bridges; - return true; // Changed - } - - private synchronized TorState getState() { - if (!started || stopped) return STARTING_STOPPING; - if (!networkInitialised) return CONNECTING; - if (!networkEnabled) return DISABLED; - return bootstrapPercentage == 100 && circuitBuilt - && orConnectionsConnected > 0 ? CONNECTED : CONNECTING; - } - - private synchronized void onOrConnectionConnected() { - int oldConnected = orConnectionsConnected; - orConnectionsConnected++; - logOrConnections(); - if (oldConnected == 0) updateState(); - } - - private synchronized void onOrConnectionClosed() { - int oldConnected = orConnectionsConnected; - orConnectionsConnected--; - if (orConnectionsConnected < 0) { - LOG.warning("Count was zero before connection closed"); - orConnectionsConnected = 0; - } - logOrConnections(); - if (orConnectionsConnected == 0 && oldConnected != 0) { - updateState(); - } - } - - @GuardedBy("this") - private void logOrConnections() { - if (LOG.isLoggable(INFO)) { - LOG.info(orConnectionsConnected + " OR connections connected"); - } - } - - private synchronized void onHsDescriptorUploaded(String onion) { - if (observer != null) { - // Notify the observer on the event executor - eventExecutor.execute(() -> - observer.onHsDescriptorUpload(onion)); - } - } - } -} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProvider.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProvider.java deleted file mode 100644 index ac0ee85d0..000000000 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProvider.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.briarproject.bramble.plugin.tor.wrapper; - -import org.briarproject.bramble.api.lifecycle.IoExecutor; -import org.briarproject.nullsafety.NotNullByDefault; - -import java.util.List; - -import javax.annotation.concurrent.ThreadSafe; - -@ThreadSafe -@NotNullByDefault -public interface CircumventionProvider { - - enum BridgeType { - DEFAULT_OBFS4, - NON_DEFAULT_OBFS4, - VANILLA, - MEEK, - SNOWFLAKE - } - - /** - * Countries where Tor is blocked, i.e. vanilla Tor connection won't work. - *

- * See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 - * and https://trac.torproject.org/projects/tor/wiki/doc/OONI/censorshipwiki - */ - String[] BLOCKED = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"}; - - /** - * Countries where bridge connections are likely to work. - * Should be a subset of {@link #BLOCKED} and the union of - * {@link #DEFAULT_BRIDGES}, {@link #NON_DEFAULT_BRIDGES} and - * {@link #DPI_BRIDGES}. - */ - String[] BRIDGES = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"}; - - /** - * Countries where default obfs4 or vanilla bridges are likely to work. - * Should be a subset of {@link #BRIDGES}. - */ - String[] DEFAULT_BRIDGES = {"EG", "VE"}; - - /** - * Countries where non-default obfs4 or vanilla bridges are likely to work. - * Should be a subset of {@link #BRIDGES}. - */ - String[] NON_DEFAULT_BRIDGES = {"BY", "RU"}; - - /** - * Countries where vanilla bridges are blocked via DPI but non-default - * obfs4 bridges, meek and snowflake may work. Should be a subset of - * {@link #BRIDGES}. - */ - String[] DPI_BRIDGES = {"CN", "IR", "TM"}; - - /** - * Returns true if vanilla Tor connections are blocked in the given country. - */ - boolean isTorProbablyBlocked(String countryCode); - - /** - * Returns true if bridge connections of some type work in the given - * country. - */ - boolean doBridgesWork(String countryCode); - - /** - * Returns the types of bridge connection that are suitable for the given - * country, or {@link #DEFAULT_BRIDGES} if no bridge type is known - * to work. - */ - List getSuitableBridgeTypes(String countryCode); - - @IoExecutor - List getBridges(BridgeType type, String countryCode, - boolean letsEncrypt); -} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProviderImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProviderImpl.java deleted file mode 100644 index a5a56df8a..000000000 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProviderImpl.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.briarproject.bramble.plugin.tor.wrapper; - -import org.briarproject.bramble.api.lifecycle.IoExecutor; -import org.briarproject.nullsafety.NotNullByDefault; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Scanner; -import java.util.Set; -import java.util.TreeMap; - -import javax.annotation.concurrent.Immutable; -import javax.inject.Inject; - -import static java.util.Arrays.asList; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.DEFAULT_OBFS4; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.MEEK; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.SNOWFLAKE; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.VANILLA; -import static org.briarproject.nullsafety.NullSafety.requireNonNull; - -@Immutable -@NotNullByDefault -class CircumventionProviderImpl implements CircumventionProvider { - - private final static String BRIDGE_FILE_NAME = "bridges"; - private final static String SNOWFLAKE_PARAMS_FILE_NAME = "snowflake-params"; - private final static String DEFAULT_COUNTRY_CODE = "ZZ"; - - private static final Set BLOCKED_IN_COUNTRIES = - new HashSet<>(asList(BLOCKED)); - private static final Set BRIDGE_COUNTRIES = - new HashSet<>(asList(BRIDGES)); - private static final Set DEFAULT_OBFS4_BRIDGE_COUNTRIES = - new HashSet<>(asList(DEFAULT_BRIDGES)); - private static final Set NON_DEFAULT_BRIDGE_COUNTRIES = - new HashSet<>(asList(NON_DEFAULT_BRIDGES)); - private static final Set DPI_COUNTRIES = - new HashSet<>(asList(DPI_BRIDGES)); - - @Inject - CircumventionProviderImpl() { - } - - @Override - public boolean isTorProbablyBlocked(String countryCode) { - return BLOCKED_IN_COUNTRIES.contains(countryCode); - } - - @Override - public boolean doBridgesWork(String countryCode) { - return BRIDGE_COUNTRIES.contains(countryCode); - } - - @Override - public List getSuitableBridgeTypes(String countryCode) { - if (DEFAULT_OBFS4_BRIDGE_COUNTRIES.contains(countryCode)) { - return asList(DEFAULT_OBFS4, VANILLA); - } else if (NON_DEFAULT_BRIDGE_COUNTRIES.contains(countryCode)) { - return asList(NON_DEFAULT_OBFS4, VANILLA); - } else if (DPI_COUNTRIES.contains(countryCode)) { - return asList(NON_DEFAULT_OBFS4, MEEK, SNOWFLAKE); - } else { - return asList(DEFAULT_OBFS4, VANILLA); - } - } - - @Override - @IoExecutor - public List getBridges(BridgeType type, String countryCode, - boolean letsEncrypt) { - InputStream is = requireNonNull(getClass().getClassLoader() - .getResourceAsStream(BRIDGE_FILE_NAME)); - Scanner scanner = new Scanner(is); - - List bridges = new ArrayList<>(); - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if ((type == DEFAULT_OBFS4 && line.startsWith("d ")) || - (type == NON_DEFAULT_OBFS4 && line.startsWith("n ")) || - (type == VANILLA && line.startsWith("v ")) || - (type == MEEK && line.startsWith("m "))) { - bridges.add(line.substring(2)); - } else if (type == SNOWFLAKE && line.startsWith("s ")) { - String params = getSnowflakeParams(countryCode, letsEncrypt); - bridges.add(line.substring(2) + " " + params); - } - } - scanner.close(); - return bridges; - } - - // Package access for testing - @SuppressWarnings("WeakerAccess") - String getSnowflakeParams(String countryCode, boolean letsEncrypt) { - Map params = loadSnowflakeParams(); - if (countryCode.isEmpty()) countryCode = DEFAULT_COUNTRY_CODE; - // If we have parameters for this country code, return them - String value = params.get(makeKey(countryCode, letsEncrypt)); - if (value != null) return value; - // Return the default parameters - value = params.get(makeKey(DEFAULT_COUNTRY_CODE, letsEncrypt)); - return requireNonNull(value); - } - - private Map loadSnowflakeParams() { - InputStream is = requireNonNull(getClass().getClassLoader() - .getResourceAsStream(SNOWFLAKE_PARAMS_FILE_NAME)); - Scanner scanner = new Scanner(is); - Map params = new TreeMap<>(); - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (line.length() < 5) continue; - String key = line.substring(0, 4); // Country code, space, digit - String value = line.substring(5); - params.put(key, value); - } - scanner.close(); - return params; - } - - private String makeKey(String countryCode, boolean letsEncrypt) { - return countryCode + " " + (letsEncrypt ? "1" : "0"); - } -} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/TorUtils.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/TorUtils.java deleted file mode 100644 index d83d3feb9..000000000 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/TorUtils.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.briarproject.bramble.plugin.tor.wrapper; - -import org.briarproject.nullsafety.NotNullByDefault; - -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.nio.charset.Charset; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.annotation.Nullable; - -import static java.util.logging.Level.WARNING; -import static org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.LOG; - -@NotNullByDefault -class TorUtils { - - @SuppressWarnings("CharsetObjectCanBeUsed") - static final Charset UTF_8 = Charset.forName("UTF-8"); - - static String scrubOnion(String onion) { - // Keep first three characters of onion address - return onion.substring(0, 3) + "[scrubbed]"; - } - - static void copyAndClose(InputStream in, OutputStream out) { - byte[] buf = new byte[4096]; - try { - while (true) { - int read = in.read(buf); - if (read == -1) break; - out.write(buf, 0, read); - } - in.close(); - out.flush(); - out.close(); - } catch (IOException e) { - tryToClose(in, LOG, WARNING); - tryToClose(out, LOG, WARNING); - } - } - - static void tryToClose(@Nullable Closeable c, Logger logger, Level level) { - try { - if (c != null) c.close(); - } catch (IOException e) { - logException(logger, level, e); - } - } - - static void tryToClose(@Nullable Socket s, Logger logger, Level level) { - try { - if (s != null) s.close(); - } catch (IOException e) { - logException(logger, level, e); - } - } - - private static void logException(Logger logger, Level level, Throwable t) { - if (logger.isLoggable(level)) logger.log(level, t.toString(), t); - } -} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/TorWrapper.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/TorWrapper.java deleted file mode 100644 index 01a94fe23..000000000 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/TorWrapper.java +++ /dev/null @@ -1,162 +0,0 @@ -package org.briarproject.bramble.plugin.tor.wrapper; - -import org.briarproject.nullsafety.NotNullByDefault; - -import java.io.IOException; -import java.util.List; -import java.util.logging.Logger; - -import javax.annotation.Nullable; - -import static java.util.logging.Logger.getLogger; - -@NotNullByDefault -public interface TorWrapper { - - Logger LOG = getLogger(TorWrapper.class.getName()); - - /** - * Starts the Tor process. - *

- * This method must only be called once. To restart the Tor process, stop - * this wrapper instance and then create a new instance. - */ - void start() throws IOException, InterruptedException; - - /** - * Tell the Tor process to stop and returns without waiting for the - * process to exit. - */ - void stop() throws IOException; - - /** - * Sets an observer for observing the state of the wrapper, replacing any - * existing observer, or removes any existing observer if the argument is - * null. - */ - void setObserver(@Nullable Observer observer); - - /** - * Returns the current state of the wrapper. - */ - TorState getTorState(); - - /** - * Returns true if the wrapper has been {@link #start() started} and not - * yet {@link #stop()} stopped. - */ - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - boolean isTorRunning(); - - /** - * Publishes an ephemeral hidden service. - * - * @param localPort The local port on which the service is listening. - * @param remotePort The port number that clients of the service will see. - * @param privateKey The private key of the hidden service, in the form - * returned by a previous call to this method, or null if a new service - * should be created. - */ - HiddenServiceProperties publishHiddenService(int localPort, - int remotePort, @Nullable String privateKey) throws IOException; - - /** - * Removes (unpublishes) an ephemeral hidden service that was created by - * calling {@link #publishHiddenService(int, int, String)}. - */ - void removeHiddenService(String onion) throws IOException; - - /** - * Enables or disables the Tor process's network connection. The network - * connection is disabled by default. - */ - void enableNetwork(boolean enable) throws IOException; - - /** - * Configures Tor to use the given list of bridges for connecting to the - * Tor network. Bridges are not used by default. - *

- * Each item in the list should be a bridge line in the same - * format that would be used in a torrc file (including the Bridge keyword). - */ - void enableBridges(List bridges) throws IOException; - - /** - * Configures Tor not to use bridges for connecting to the Tor network. - * Bridges are not used by default. - */ - void disableBridges() throws IOException; - - /** - * Enables or disables connection padding. Padding is disabled by default. - */ - void enableConnectionPadding(boolean enable) throws IOException; - - /** - * Configures Tor to use IPv6 or IPv4 for connecting to the Tor network. - * IPv4 is used by default. - */ - void enableIpv6(boolean ipv6Only) throws IOException; - - /** - * The state of the Tor wrapper. - */ - enum TorState { - - /** - * The Tor process is either starting or stopping. - */ - STARTING_STOPPING, - - /** - * The Tor process has started, its network connection is enabled, and - * it is connecting (or reconnecting) to the Tor network. - */ - CONNECTING, - - /** - * The Tor process has started, its network connection is enabled, and - * it has connected to the Tor network. In this state it should be - * possible to make connections via the SOCKS port. - */ - CONNECTED, - - /** - * The Tor process has started but its network connection is disabled. - */ - DISABLED - } - - /** - * An interface for observing changes to the {@link TorState state} of the - * Tor process. All calls happen on the event executor supplied to the - * wrapper's constructor. - */ - interface Observer { - - /** - * Called whenever the state of the Tor process changes. - */ - void onState(TorState s); - - /** - * Called whenever the bootstrap percentage changes. - */ - void onBootstrapPercentage(int percentage); - - /** - * Called whenever a hidden service descriptor is uploaded. - */ - void onHsDescriptorUpload(String onion); - } - - class HiddenServiceProperties { - - public final String onion, privKey; - - HiddenServiceProperties(String onion, String privKey) { - this.onion = onion; - this.privKey = privKey; - } - } -} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionModule.java b/bramble-core/src/main/java/org/briarproject/onionwrapper/CircumventionModule.java similarity index 83% rename from bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionModule.java rename to bramble-core/src/main/java/org/briarproject/onionwrapper/CircumventionModule.java index be4249f48..0c60cf01a 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionModule.java +++ b/bramble-core/src/main/java/org/briarproject/onionwrapper/CircumventionModule.java @@ -1,4 +1,4 @@ -package org.briarproject.bramble.plugin.tor.wrapper; +package org.briarproject.onionwrapper; import javax.inject.Singleton; diff --git a/bramble-core/src/main/resources/bridges b/bramble-core/src/main/resources/bridges deleted file mode 100644 index e365f5242..000000000 --- a/bramble-core/src/main/resources/bridges +++ /dev/null @@ -1,35 +0,0 @@ -d Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1 -d Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0 -d Bridge obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0 -d Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0 -d Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0 -d Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0 -d Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0 -d Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0 -d Bridge obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0 -d Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0 -d Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0 -n Bridge obfs4 185.181.11.86:443 A961609729E7FDF520B4E81F1F1B8FA1045285C3 cert=e5faG9Zk4Ni+e7z2YgGfevyKPQlMvkVGi4ublSsHYjaBovKeNXpOhbeFxzbZZoAzxAoGUQ iat-mode=0 -n Bridge obfs4 120.29.217.52:5223 40FE3DB9800272F9CDC76422F8ED7883280EE96D cert=/71PS4l8c/XJ4DIItlH9xMqNvPFg2RUTrHvPlQWh48u5et8h/yyyjCcYphUadDsfBWpaGQ iat-mode=0 -n Bridge obfs4 185.177.207.138:8443 53716FE26F23C8C6A71A2BC5D9D8DC64747278C7 cert=6jcYVilMEzxdsWghSrQFpYYJlkkir/GPIXw/EnddUK3S8ToVpMG8u1SwMOEdEs735RrMHw iat-mode=0 -n Bridge obfs4 45.142.181.131:42069 6EBCF6B02DA2B982F4080A7119D737366AFB74FA cert=9HyWH/BCwWzNirZdTQtluCgJk+gFhpOqydIuyQ1iDvpnqsAynKF+zcPE/INZFefm86UlBg iat-mode=0 -n Bridge obfs4 152.67.77.101:4096 B82DB9CDDF887AB8A859420E07DF298E30AF8A6E cert=21OWn3yFo+hulmQNAOtF5uwwOqWtdT5PrLhk8BG9DpOd0/k5DEkQEYPyDdXbS9nZ0E5BJA iat-mode=0 -n Bridge obfs4 94.142.246.132:8088 135C158527AA9FE9A2F26EC515EB6999D813D347 cert=wTUz0/5FhAZRkitil5MprGbSF3JzjxjxI1kAmxAdSeDy98NgcLr11f/qUXWDC76Y97RiSg iat-mode=0 -n Bridge obfs4 152.70.180.20:1993 3327C43587E66AD5F874C4234A1D72C938AD7318 cert=s7xLRUO2psaX7TMUP2YhXdxItR4U6K7D+E3gQaS/+yWUppevtazIibq4dN1g5Reu6dD2QQ iat-mode=0 -n Bridge obfs4 144.202.12.254:10002 4E220F45CD404C8A3082A36326A5ED19BB8D4404 cert=iLz5YYWO4pUw7U7MRNOSvE0qO+IVeE4kVfFVWPO3coH3FmZtrkvlaTklfXxHZaCcXWBgaA iat-mode=0 -n Bridge obfs4 109.14.168.159:5082 BFE1416DEFFE969581F016A4A319A87FFB26BA91 cert=n3X1CDdKBPXPIzfKh83p3ydfMzb0AD9gKC+/gIpHb7+xjjAnYO9x3LT+T/MvOIfAXxYySg iat-mode=0 -n Bridge obfs4 185.177.207.132:8443 4FB781F7A9DD39DA53A7996907817FC479874D19 cert=UL2gCAXWW5kEWY4TQ0lNeu6OAmzh40bXYVhMnTWVG8USnyy/zEKGSIPgmwTDMumWr9c1Pg iat-mode=0 -n Bridge obfs4 213.108.110.149:7499 519344140473CF91030B08F91521F9A6C144ED6C cert=k9fSL/d491qAkGmi2VeSwVlfuyO02jBeN54qxzzQISxpfm3b+a6kJpo8/Bfy1ACbHZIJUg iat-mode=0 -n Bridge obfs4 158.174.114.97:3456 32665CD4CBE19092CA47A53D317B8BFF5810441C cert=ne5Zt0TcMedSGmFwAs/AV6J6E9Hn7mG5mR6vQNpEfyuCZK1VRpQvU1LvvtesSu4CXqZtYQ iat-mode=0 -n Bridge obfs4 64.4.175.62:8000 8B72740D150795ACB5101AA5F95D1ACDA4FE6B3E cert=vduuNhJ5U/8hjZmllP6AFfXSlSZsnrimdR8Tm8DY9dxWS4n2j92fNc0qHihUwRqwcOfIcg iat-mode=0 -n Bridge obfs4 82.64.115.17:990 B08238781C2CD80DBD95AEABEB6F6C75F2E2CEB6 cert=1udeMlFNs3sJ20zwpPE6nShZqqwDb3F1ET4KzfSfD+fktkue9zNx9H3t+yLCPAsg+6UTUA iat-mode=1 -n Bridge obfs4 87.161.120.147:9292 9418EEBE8AEAE32CC381AF51610366E8B24651E0 cert=DFRm74qsD1i2/ypaGochpX6CS1j9JTFAKEYaHXrgrx6M2LG5Cvppdt3Ob7lULfhqgtAUdg iat-mode=0 -n Bridge obfs4 157.90.245.231:8599 C23CD468EC04555E2B37BE81A771E681049DEA6A cert=UsmDelrbwg4jc6BMvZJ0TS8klUIa2qkbRu3xwQc3ZXPEgpMqyTYUxcVwyPbIU5KmAHsmAA iat-mode=0 -v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F -v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31 -v Bridge 213.196.191.96:9060 05E222E2A8C234234FE0CEB58B08A93B8FC360DB -v Bridge 75.100.92.154:22815 465E990FA8A752DDE861EDF31E42454ACC037AB4 -v Bridge 87.100.193.2:9010 13FB63452AADFA082BD2BC3E1E320AD301F07877 -v Bridge 65.21.240.163:33245 20BD59649212CFE7412BFC9B94C3CCCFD8F807A8 -m Bridge meek_lite 192.0.2.18:80 BE776A53492E1E044A26F17306E1BC46A55A1625 url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com utls=hellochrome_auto -s Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 diff --git a/bramble-core/src/main/resources/snowflake-params b/bramble-core/src/main/resources/snowflake-params deleted file mode 100644 index a6cbd8563..000000000 --- a/bramble-core/src/main/resources/snowflake-params +++ /dev/null @@ -1,4 +0,0 @@ -ZZ 1 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellochrome_auto -ZZ 0 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellochrome_auto -TM 1 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479 utls-imitate=hellochrome_auto -TM 0 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479 utls-imitate=hellochrome_auto diff --git a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProviderImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProviderImplTest.java deleted file mode 100644 index 374116e45..000000000 --- a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tor/wrapper/CircumventionProviderImplTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.briarproject.bramble.plugin.tor.wrapper; - -import org.briarproject.bramble.test.BrambleTestCase; -import org.junit.Test; - -import java.util.HashSet; -import java.util.Set; - -import static java.util.Arrays.asList; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BLOCKED; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BRIDGES; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.DEFAULT_OBFS4; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.MEEK; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.SNOWFLAKE; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.VANILLA; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.DEFAULT_BRIDGES; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.DPI_BRIDGES; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.NON_DEFAULT_BRIDGES; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; - -public class CircumventionProviderImplTest extends BrambleTestCase { - - private final CircumventionProviderImpl provider = - new CircumventionProviderImpl(); - - @Test - public void testInvariants() { - Set blocked = new HashSet<>(asList(BLOCKED)); - Set bridges = new HashSet<>(asList(BRIDGES)); - Set defaultBridges = new HashSet<>(asList(DEFAULT_BRIDGES)); - Set nonDefaultBridges = - new HashSet<>(asList(NON_DEFAULT_BRIDGES)); - Set dpiBridges = new HashSet<>(asList(DPI_BRIDGES)); - // BRIDGES should be a subset of BLOCKED - assertTrue(blocked.containsAll(bridges)); - // BRIDGES should be the union of the bridge type sets - Set union = new HashSet<>(defaultBridges); - union.addAll(nonDefaultBridges); - union.addAll(dpiBridges); - assertEquals(bridges, union); - // The bridge type sets should not overlap - assertEmptyIntersection(defaultBridges, nonDefaultBridges); - assertEmptyIntersection(defaultBridges, dpiBridges); - assertEmptyIntersection(nonDefaultBridges, dpiBridges); - } - - @Test - public void testGetBestBridgeType() { - for (String country : DEFAULT_BRIDGES) { - assertEquals(asList(DEFAULT_OBFS4, VANILLA), - provider.getSuitableBridgeTypes(country)); - } - for (String country : NON_DEFAULT_BRIDGES) { - assertEquals(asList(NON_DEFAULT_OBFS4, VANILLA), - provider.getSuitableBridgeTypes(country)); - } - for (String country : DPI_BRIDGES) { - assertEquals(asList(NON_DEFAULT_OBFS4, MEEK, SNOWFLAKE), - provider.getSuitableBridgeTypes(country)); - } - assertEquals(asList(DEFAULT_OBFS4, VANILLA), - provider.getSuitableBridgeTypes("ZZ")); - } - - @Test - public void testHasSnowflakeParamsWithLetsEncrypt() { - testHasSnowflakeParams(true); - } - - @Test - public void testHasSnowflakeParamsWithoutLetsEncrypt() { - testHasSnowflakeParams(false); - } - - private void testHasSnowflakeParams(boolean letsEncrypt) { - String tmParams = provider.getSnowflakeParams("TM", letsEncrypt); - String defaultParams = provider.getSnowflakeParams("ZZ", letsEncrypt); - assertFalse(tmParams.isEmpty()); - assertFalse(defaultParams.isEmpty()); - assertNotEquals(defaultParams, tmParams); - } - - private void assertEmptyIntersection(Set a, Set b) { - Set intersection = new HashSet<>(a); - intersection.retainAll(b); - assertTrue(intersection.isEmpty()); - } -} diff --git a/bramble-java/build.gradle b/bramble-java/build.gradle index 29586169a..124e12a09 100644 --- a/bramble-java/build.gradle +++ b/bramble-java/build.gradle @@ -9,16 +9,13 @@ apply from: '../dagger.gradle' dependencies { implementation project(':bramble-core') + implementation project(':onionwrapper-java') implementation fileTree(dir: 'libs', include: '*.jar') def jna_version = '4.5.2' implementation "net.java.dev.jna:jna:$jna_version" implementation "net.java.dev.jna:jna-platform:$jna_version" - testImplementation "org.briarproject:tor-linux:$tor_version" - testImplementation "org.briarproject:obfs4proxy-linux:$obfs4proxy_version" - testImplementation "org.briarproject:snowflake-linux:$snowflake_version" - annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version" testImplementation project(path: ':bramble-api', configuration: 'testOutput') @@ -27,9 +24,6 @@ dependencies { testImplementation "junit:junit:$junit_version" testImplementation "org.jmock:jmock:$jmock_version" testImplementation "org.jmock:jmock-junit4:$jmock_version" - testImplementation "com.squareup.okhttp3:okhttp:$okhttp_version" - - testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version" } tasks.withType(Test) { diff --git a/bramble-java/src/main/java/org/briarproject/bramble/BrambleJavaModule.java b/bramble-java/src/main/java/org/briarproject/bramble/BrambleJavaModule.java index 2d4fd9376..b6f4a5627 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/BrambleJavaModule.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/BrambleJavaModule.java @@ -3,9 +3,9 @@ package org.briarproject.bramble; import org.briarproject.bramble.io.DnsModule; import org.briarproject.bramble.mailbox.ModularMailboxModule; import org.briarproject.bramble.network.JavaNetworkModule; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionModule; import org.briarproject.bramble.socks.SocksModule; import org.briarproject.bramble.system.JavaSystemModule; +import org.briarproject.onionwrapper.CircumventionModule; import dagger.Module; diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPluginFactory.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPluginFactory.java index a9b48f1a2..6f3b58501 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPluginFactory.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPluginFactory.java @@ -15,10 +15,10 @@ import org.briarproject.bramble.api.plugin.TorSocksPort; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.WakefulIoExecutor; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider; -import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper; -import org.briarproject.bramble.plugin.tor.wrapper.UnixTorWrapper; import org.briarproject.nullsafety.NotNullByDefault; +import org.briarproject.onionwrapper.CircumventionProvider; +import org.briarproject.onionwrapper.TorWrapper; +import org.briarproject.onionwrapper.UnixTorWrapper; import java.io.File; import java.util.concurrent.Executor; diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/WindowsTorPluginFactory.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/WindowsTorPluginFactory.java index 23ba5ba56..456dcc4bd 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/WindowsTorPluginFactory.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/WindowsTorPluginFactory.java @@ -15,10 +15,10 @@ import org.briarproject.bramble.api.plugin.TorSocksPort; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.WakefulIoExecutor; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider; -import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper; -import org.briarproject.bramble.plugin.tor.wrapper.WindowsTorWrapper; import org.briarproject.nullsafety.NotNullByDefault; +import org.briarproject.onionwrapper.CircumventionProvider; +import org.briarproject.onionwrapper.TorWrapper; +import org.briarproject.onionwrapper.WindowsTorWrapper; import java.io.File; import java.util.concurrent.Executor; diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/JavaTorWrapper.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/JavaTorWrapper.java deleted file mode 100644 index 9d8ff3e9b..000000000 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/JavaTorWrapper.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.briarproject.bramble.plugin.tor.wrapper; - -import org.briarproject.nullsafety.NotNullByDefault; - -import java.io.File; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.security.CodeSource; -import java.util.concurrent.Executor; - -import static org.briarproject.nullsafety.NullSafety.requireNonNull; - -@NotNullByDefault -abstract class JavaTorWrapper extends AbstractTorWrapper { - - JavaTorWrapper(Executor ioExecutor, - Executor eventExecutor, - String architecture, - File torDirectory, - int torSocksPort, - int torControlPort) { - super(ioExecutor, eventExecutor, architecture, torDirectory, - torSocksPort, torControlPort); - } - - @Override - protected long getLastUpdateTime() { - CodeSource codeSource = - getClass().getProtectionDomain().getCodeSource(); - if (codeSource == null) throw new AssertionError("CodeSource null"); - try { - URI path = codeSource.getLocation().toURI(); - File file = new File(path); - return file.lastModified(); - } catch (URISyntaxException e) { - throw new AssertionError(e); - } - } - - @Override - protected InputStream getResourceInputStream(String name, - String extension) { - ClassLoader cl = getClass().getClassLoader(); - return requireNonNull(cl.getResourceAsStream(name + extension)); - } -} diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/UnixTorWrapper.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/UnixTorWrapper.java deleted file mode 100644 index d6f599c78..000000000 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/UnixTorWrapper.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.briarproject.bramble.plugin.tor.wrapper; - -import com.sun.jna.Library; -import com.sun.jna.Native; - -import org.briarproject.nullsafety.NotNullByDefault; - -import java.io.File; -import java.util.concurrent.Executor; - -/** - * A Tor wrapper for Unix-like operating systems. - */ -@NotNullByDefault -public class UnixTorWrapper extends JavaTorWrapper { - - /** - * @param ioExecutor The wrapper will use this executor to run IO tasks, - * some of which may run for the lifetime of the wrapper, so the executor - * should have an unlimited thread pool. - * @param eventExecutor The wrapper will use this executor to call the - * {@link Observer observer} (if any). To ensure that events are observed - * in the order they occur, this executor should have a single thread (eg - * the app's main thread). - * @param architecture The processor architecture of the Tor and pluggable - * transport binaries. - * @param torDirectory The directory where the Tor process should keep its - * state. - * @param torSocksPort The port number to use for Tor's SOCKS port. - * @param torControlPort The port number to use for Tor's control port. - */ - public UnixTorWrapper(Executor ioExecutor, - Executor eventExecutor, - String architecture, - File torDirectory, - int torSocksPort, - int torControlPort) { - super(ioExecutor, eventExecutor, architecture, torDirectory, - torSocksPort, torControlPort); - } - - @Override - protected int getProcessId() { - return CLibrary.INSTANCE.getpid(); - } - - private interface CLibrary extends Library { - - CLibrary INSTANCE = Native.loadLibrary("c", CLibrary.class); - - int getpid(); - } -} diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/WindowsTorWrapper.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/WindowsTorWrapper.java deleted file mode 100644 index 3807786b5..000000000 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/wrapper/WindowsTorWrapper.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.briarproject.bramble.plugin.tor.wrapper; - -import com.sun.jna.platform.win32.Kernel32; - -import org.briarproject.nullsafety.NotNullByDefault; - -import java.io.File; -import java.io.IOException; -import java.util.Scanner; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Executor; - -import static java.util.logging.Level.INFO; - -/** - * A Tor wrapper for the Windows operating system. - */ -@NotNullByDefault -public class WindowsTorWrapper extends JavaTorWrapper { - - /** - * @param ioExecutor The wrapper will use this executor to run IO tasks, - * some of which may run for the lifetime of the wrapper, so the executor - * should have an unlimited thread pool. - * @param eventExecutor The wrapper will use this executor to call - * @param eventExecutor The wrapper will use this executor to call the - * {@link Observer observer} (if any). To ensure that events are observed - * in the order they occur, this executor should have a single thread (eg - * the app's main thread). - * @param architecture The processor architecture of the Tor and pluggable - * transport binaries. - * @param torDirectory The directory where the Tor process should keep its - * state. - * @param torSocksPort The port number to use for Tor's SOCKS port. - * @param torControlPort The port number to use for Tor's control port. - */ - public WindowsTorWrapper(Executor ioExecutor, - Executor eventExecutor, - String architecture, - File torDirectory, - int torSocksPort, - int torControlPort) { - super(ioExecutor, eventExecutor, architecture, torDirectory, - torSocksPort, torControlPort); - } - - @Override - protected int getProcessId() { - return Kernel32.INSTANCE.GetCurrentProcessId(); - } - - @Override - protected void waitForTorToStart(Process torProcess) - throws InterruptedException, IOException { - // On Windows the RunAsDaemon option has no effect, so Tor won't detach. - // Wait for the control port to be opened, then continue to read its - // stdout and stderr in a background thread until it exits. - BlockingQueue success = new ArrayBlockingQueue<>(1); - ioExecutor.execute(() -> { - boolean started = false; - // Read the process's stdout (and redirected stderr) - Scanner stdout = new Scanner(torProcess.getInputStream()); - // Log the first line of stdout (contains Tor and library versions) - if (stdout.hasNextLine()) LOG.info(stdout.nextLine()); - // Startup has succeeded when the control port is open - while (stdout.hasNextLine()) { - String line = stdout.nextLine(); - if (!started && line.contains("Opened Control listener")) { - success.add(true); - started = true; - } - } - stdout.close(); - // If the control port wasn't opened, startup has failed - if (!started) success.add(false); - // Wait for the process to exit - try { - int exit = torProcess.waitFor(); - if (LOG.isLoggable(INFO)) - LOG.info("Tor exited with value " + exit); - } catch (InterruptedException e1) { - LOG.warning("Interrupted while waiting for Tor to exit"); - Thread.currentThread().interrupt(); - } - }); - // Wait for the startup result - if (!success.take()) throw new IOException(); - } - - @Override - protected String getExecutableExtension() { - return ".exe"; - } -} diff --git a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java deleted file mode 100644 index f5bb53141..000000000 --- a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java +++ /dev/null @@ -1,288 +0,0 @@ -package org.briarproject.bramble.plugin.tor; - -import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons; -import org.briarproject.bramble.api.Multiset; -import org.briarproject.bramble.api.battery.BatteryManager; -import org.briarproject.bramble.api.crypto.CryptoComponent; -import org.briarproject.bramble.api.event.EventBus; -import org.briarproject.bramble.api.event.EventExecutor; -import org.briarproject.bramble.api.lifecycle.IoExecutor; -import org.briarproject.bramble.api.network.NetworkManager; -import org.briarproject.bramble.api.plugin.BackoffFactory; -import org.briarproject.bramble.api.plugin.TorControlPort; -import org.briarproject.bramble.api.plugin.TorSocksPort; -import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; -import org.briarproject.bramble.api.system.Clock; -import org.briarproject.bramble.api.system.LocationUtils; -import org.briarproject.bramble.api.system.ResourceProvider; -import org.briarproject.bramble.api.system.WakefulIoExecutor; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType; -import org.briarproject.bramble.test.BrambleJavaIntegrationTestComponent; -import org.briarproject.bramble.test.BrambleTestCase; -import org.briarproject.bramble.test.DaggerBrambleJavaIntegrationTestComponent; -import org.briarproject.bramble.util.OsUtils; -import org.briarproject.nullsafety.NotNullByDefault; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.concurrent.Executor; -import java.util.logging.Logger; - -import javax.annotation.concurrent.GuardedBy; -import javax.inject.Inject; -import javax.net.SocketFactory; - -import static java.util.Collections.singletonList; -import static java.util.concurrent.TimeUnit.MINUTES; -import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.DEFAULT_OBFS4; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.MEEK; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.SNOWFLAKE; -import static org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider.BridgeType.VANILLA; -import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; -import static org.briarproject.bramble.test.TestUtils.getTestDirectory; -import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; - -@RunWith(Parameterized.class) -public class BridgeTest extends BrambleTestCase { - - private static final String[] SNOWFLAKE_COUNTRY_CODES = {"TM", "ZZ"}; - - @Parameters - public static Iterable data() { - BrambleJavaIntegrationTestComponent component = - DaggerBrambleJavaIntegrationTestComponent.builder().build(); - BrambleCoreIntegrationTestEagerSingletons.Helper - .injectEagerSingletons(component); - // Share stats among all the test instances - Stats stats = new Stats(); - CircumventionProvider provider = component.getCircumventionProvider(); - List states = new ArrayList<>(); - for (int i = 0; i < ATTEMPTS_PER_BRIDGE; i++) { - for (String bridge : - provider.getBridges(DEFAULT_OBFS4, "", true)) { - states.add(new Params(bridge, DEFAULT_OBFS4, stats, false)); - } - for (String bridge : - provider.getBridges(NON_DEFAULT_OBFS4, "", true)) { - states.add(new Params(bridge, NON_DEFAULT_OBFS4, stats, - false)); - } - for (String bridge : provider.getBridges(VANILLA, "", true)) { - states.add(new Params(bridge, VANILLA, stats, false)); - } - for (String bridge : provider.getBridges(MEEK, "", true)) { - states.add(new Params(bridge, MEEK, stats, true)); - } - for (String countryCode : SNOWFLAKE_COUNTRY_CODES) { - for (String bridge : - provider.getBridges(SNOWFLAKE, countryCode, true)) { - states.add(new Params(bridge, SNOWFLAKE, stats, true)); - } - for (String bridge : - provider.getBridges(SNOWFLAKE, countryCode, false)) { - states.add(new Params(bridge, SNOWFLAKE, stats, true)); - } - } - } - return states; - } - - private final static long TIMEOUT = MINUTES.toMillis(2); - private final static long MEEK_TIMEOUT = MINUTES.toMillis(6); - private final static int UNREACHABLE_BRIDGES_ALLOWED = 6; - private final static int ATTEMPTS_PER_BRIDGE = 5; - - private final static Logger LOG = getLogger(BridgeTest.class.getName()); - - @Inject - @IoExecutor - Executor ioExecutor; - @Inject - @EventExecutor - Executor eventExecutor; - @Inject - @WakefulIoExecutor - Executor wakefulIoExecutor; - @Inject - NetworkManager networkManager; - @Inject - ResourceProvider resourceProvider; - @Inject - BatteryManager batteryManager; - @Inject - EventBus eventBus; - @Inject - BackoffFactory backoffFactory; - @Inject - Clock clock; - @Inject - CryptoComponent crypto; - @Inject - @TorSocksPort - int torSocksPort; - @Inject - @TorControlPort - int torControlPort; - - private final File torDir = getTestDirectory(); - private final Params params; - - private UnixTorPluginFactory factory; - - public BridgeTest(Params params) { - this.params = params; - } - - @Before - public void setUp() { - // Skip this test unless it's explicitly enabled in the environment - assumeTrue(isOptionalTestEnabled(BridgeTest.class)); - - // TODO: Remove this assumption when the plugin supports other platforms - assumeTrue(OsUtils.isLinux()); - - BrambleJavaIntegrationTestComponent component = - DaggerBrambleJavaIntegrationTestComponent.builder().build(); - BrambleCoreIntegrationTestEagerSingletons.Helper - .injectEagerSingletons(component); - component.inject(this); - - LocationUtils locationUtils = () -> "US"; - SocketFactory torSocketFactory = SocketFactory.getDefault(); - - @NotNullByDefault - CircumventionProvider bridgeProvider = new CircumventionProvider() { - @Override - public boolean isTorProbablyBlocked(String countryCode) { - return true; - } - - @Override - public boolean doBridgesWork(String countryCode) { - return true; - } - - @Override - public List getSuitableBridgeTypes(String countryCode) { - return singletonList(params.bridgeType); - } - - @Override - public List getBridges(BridgeType bridgeType, - String countryCode, boolean letsEncrypt) { - return singletonList(params.bridge); - } - }; - factory = new UnixTorPluginFactory(ioExecutor, eventExecutor, - wakefulIoExecutor, networkManager, locationUtils, eventBus, - torSocketFactory, backoffFactory, bridgeProvider, - batteryManager, clock, crypto, torDir, torSocksPort, - torControlPort); - } - - @After - public void tearDown() { - deleteTestDirectory(torDir); - } - - @Test - public void testBridges() throws Exception { - if (params.stats.hasSucceeded(params.bridge)) { - LOG.info("Skipping previously successful bridge: " + params.bridge); - return; - } - - DuplexPlugin duplexPlugin = - factory.createPlugin(new TestPluginCallback()); - assertNotNull(duplexPlugin); - TorPlugin plugin = (TorPlugin) duplexPlugin; - - LOG.warning("Testing " + params.bridge); - try { - plugin.start(); - long start = clock.currentTimeMillis(); - long timeout = params.bridgeType == MEEK ? MEEK_TIMEOUT : TIMEOUT; - while (clock.currentTimeMillis() - start < timeout) { - if (plugin.getState() == ACTIVE) break; - clock.sleep(500); - } - if (plugin.getState() == ACTIVE) { - LOG.info("Connected to Tor: " + params.bridge); - params.stats.countSuccess(params.bridge); - } else { - LOG.warning("Could not connect to Tor within timeout: " - + params.bridge); - params.stats.countFailure(params.bridge, params.essential); - } - } finally { - plugin.stop(); - } - } - - private static class Params { - - private final String bridge; - private final BridgeType bridgeType; - private final Stats stats; - private final boolean essential; - - private Params(String bridge, BridgeType bridgeType, - Stats stats, boolean essential) { - this.bridge = bridge; - this.bridgeType = bridgeType; - this.stats = stats; - this.essential = essential; - } - } - - private static class Stats { - - @GuardedBy("this") - private final Set successes = new HashSet<>(); - @GuardedBy("this") - private final Multiset failures = new Multiset<>(); - @GuardedBy("this") - private final Set unreachable = new TreeSet<>(); - - private synchronized boolean hasSucceeded(String bridge) { - return successes.contains(bridge); - } - - private synchronized void countSuccess(String bridge) { - successes.add(bridge); - } - - private synchronized void countFailure(String bridge, - boolean essential) { - if (failures.add(bridge) == ATTEMPTS_PER_BRIDGE) { - LOG.warning("Bridge is unreachable after " - + ATTEMPTS_PER_BRIDGE + " attempts: " + bridge); - unreachable.add(bridge); - if (unreachable.size() > UNREACHABLE_BRIDGES_ALLOWED) { - fail(unreachable.size() + " bridges are unreachable: " - + unreachable); - } - if (essential) { - fail("essential bridge is unreachable"); - } - } - } - } -} diff --git a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/TestPluginCallback.java b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/TestPluginCallback.java deleted file mode 100644 index cb4cf7494..000000000 --- a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/TestPluginCallback.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.briarproject.bramble.plugin.tor; - -import org.briarproject.bramble.api.plugin.Plugin.State; -import org.briarproject.bramble.api.plugin.PluginCallback; -import org.briarproject.bramble.api.plugin.TransportConnectionReader; -import org.briarproject.bramble.api.plugin.TransportConnectionWriter; -import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; -import org.briarproject.bramble.api.properties.TransportProperties; -import org.briarproject.bramble.api.settings.Settings; -import org.briarproject.nullsafety.NotNullByDefault; - -import java.util.Collection; - -import static java.util.Collections.emptyList; - -@NotNullByDefault -public class TestPluginCallback implements PluginCallback { - - @Override - public Settings getSettings() { - return new Settings(); - } - - @Override - public TransportProperties getLocalProperties() { - return new TransportProperties(); - } - - @Override - public Collection getRemoteProperties() { - return emptyList(); - } - - @Override - public void mergeSettings(Settings s) { - } - - @Override - public void mergeLocalProperties(TransportProperties p) { - } - - @Override - public void pluginStateChanged(State state) { - } - - @Override - public void handleConnection(DuplexTransportConnection c) { - } - - @Override - public void handleReader(TransportConnectionReader r) { - } - - @Override - public void handleWriter(TransportConnectionWriter w) { - } -} diff --git a/bramble-java/src/test/java/org/briarproject/bramble/test/BrambleJavaIntegrationTestComponent.java b/bramble-java/src/test/java/org/briarproject/bramble/test/BrambleJavaIntegrationTestComponent.java deleted file mode 100644 index 8443e578c..000000000 --- a/bramble-java/src/test/java/org/briarproject/bramble/test/BrambleJavaIntegrationTestComponent.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.briarproject.bramble.test; - -import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons; -import org.briarproject.bramble.BrambleCoreModule; -import org.briarproject.bramble.BrambleJavaModule; -import org.briarproject.bramble.mailbox.ModularMailboxModule; -import org.briarproject.bramble.plugin.tor.BridgeTest; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider; - -import javax.inject.Singleton; - -import dagger.Component; - -@Singleton -@Component(modules = { - BrambleCoreIntegrationTestModule.class, - BrambleCoreModule.class, - BrambleJavaModule.class, - ModularMailboxModule.class, - TestTorPortsModule.class, - TestPluginConfigModule.class, -}) -public interface BrambleJavaIntegrationTestComponent - extends BrambleCoreIntegrationTestEagerSingletons { - - void inject(BridgeTest init); - - CircumventionProvider getCircumventionProvider(); -} diff --git a/bramble-java/src/test/java/org/briarproject/bramble/test/TestResources.java b/bramble-java/src/test/java/org/briarproject/bramble/test/TestResources.java deleted file mode 100644 index 10b52a475..000000000 --- a/bramble-java/src/test/java/org/briarproject/bramble/test/TestResources.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.briarproject.bramble.test; - -import org.briarproject.bramble.util.OsUtils; -import org.junit.Before; -import org.junit.Test; - -import java.io.InputStream; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assume.assumeTrue; - -public class TestResources { - - @Before - public void isRunningOnLinux() { - assumeTrue(OsUtils.isLinux()); - } - - @Test - public void canReadTorLinux() { - InputStream input = Thread.currentThread().getContextClassLoader() - .getResourceAsStream("x86_64/tor"); - assertNotNull(input); - } - - @Test - public void canReadObfs4ProxyLinux() { - InputStream input = Thread.currentThread().getContextClassLoader() - .getResourceAsStream("x86_64/obfs4proxy"); - assertNotNull(input); - } - - @Test - public void canReadSnowflakeLinux() { - InputStream input = Thread.currentThread().getContextClassLoader() - .getResourceAsStream("x86_64/snowflake"); - assertNotNull(input); - } - -} diff --git a/bramble-java/src/test/java/org/briarproject/bramble/test/TestTorPortsModule.java b/bramble-java/src/test/java/org/briarproject/bramble/test/TestTorPortsModule.java deleted file mode 100644 index a8f03045d..000000000 --- a/bramble-java/src/test/java/org/briarproject/bramble/test/TestTorPortsModule.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.briarproject.bramble.test; - -import org.briarproject.bramble.api.plugin.TorControlPort; -import org.briarproject.bramble.api.plugin.TorSocksPort; - -import dagger.Module; -import dagger.Provides; - -import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT; -import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT; - -@Module -class TestTorPortsModule { - - @Provides - @TorSocksPort - int provideTorSocksPort() { - return DEFAULT_SOCKS_PORT + 10; - } - - @Provides - @TorControlPort - int provideTorControlPort() { - return DEFAULT_CONTROL_PORT + 10; - } -} diff --git a/bramble-java/witness.gradle b/bramble-java/witness.gradle index 0154e0375..995552c8f 100644 --- a/bramble-java/witness.gradle +++ b/bramble-java/witness.gradle @@ -15,8 +15,6 @@ dependencyVerification { 'com.google.guava:guava:31.0.1-jre:guava-31.0.1-jre.jar:d5be94d65e87bd219fb3193ad1517baa55a3b88fc91d21cf735826ab5af087b9', 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99', 'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b', - 'com.squareup.okhttp3:okhttp:3.12.13:okhttp-3.12.13.jar:508234e024ef7e270ab1a6d5b356f5b98e786511239ca986d684fd1e2cf7bc82', - 'com.squareup.okio:okio:1.15.0:okio-1.15.0.jar:693fa319a7e8843300602b204023b7674f106ebcb577f2dd5807212b66118bd2', 'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3', @@ -26,9 +24,6 @@ dependencyVerification { 'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0', 'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd', 'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047', - 'org.briarproject:obfs4proxy-linux:0.0.14-tor2:obfs4proxy-linux-0.0.14-tor2.jar:bb2431092b5ad998ad620b0223e725c0f7e43f1b02af2f097a2544edc1fd9738', - 'org.briarproject:snowflake-linux:2.5.1:snowflake-linux-2.5.1.jar:edc807dcb7758365970d95525e4749349a27f462d0e2df6505ad1ca65fb296d2', - 'org.briarproject:tor-linux:0.4.7.13-2:tor-linux-0.4.7.13-2.jar:1e4ca9e0f724e1f17fcce570832704942cc3be26c4c2eccbe5aae29f35afa307', 'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a', 'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb', 'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9', diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java index e58baf4ad..6e8196efc 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java @@ -30,7 +30,6 @@ import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.mailbox.ModularMailboxModule; import org.briarproject.bramble.plugin.file.RemovableDriveModule; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider; import org.briarproject.bramble.system.ClockModule; import org.briarproject.briar.BriarCoreEagerSingletons; import org.briarproject.briar.BriarCoreModule; @@ -84,6 +83,7 @@ import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager; import org.briarproject.briar.api.test.TestDataCreator; +import org.briarproject.onionwrapper.CircumventionProvider; import java.util.concurrent.Executor; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsViewModel.java index 9ccafba31..e8292f81e 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsViewModel.java @@ -24,7 +24,6 @@ import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent; import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.LocationUtils; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider; import org.briarproject.briar.R; import org.briarproject.briar.android.attachment.UnsupportedMimeTypeException; import org.briarproject.briar.android.attachment.media.ImageCompressor; @@ -34,6 +33,7 @@ import org.briarproject.briar.api.identity.AuthorInfo; import org.briarproject.briar.api.identity.AuthorManager; import org.briarproject.nullsafety.MethodsNotNullByDefault; import org.briarproject.nullsafety.ParametersNotNullByDefault; +import org.briarproject.onionwrapper.CircumventionProvider; import java.io.IOException; import java.io.InputStream; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/TorSummaryProvider.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/TorSummaryProvider.java index d52ab51b0..086739306 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/TorSummaryProvider.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/TorSummaryProvider.java @@ -3,9 +3,9 @@ package org.briarproject.briar.android.settings; import android.content.Context; import org.briarproject.bramble.api.system.LocationUtils; -import org.briarproject.bramble.plugin.tor.wrapper.CircumventionProvider; import org.briarproject.briar.R; import org.briarproject.nullsafety.NotNullByDefault; +import org.briarproject.onionwrapper.CircumventionProvider; import androidx.preference.ListPreference; import androidx.preference.Preference.SummaryProvider; diff --git a/briar-headless/build.gradle b/briar-headless/build.gradle index 5545d832c..b69d75974 100644 --- a/briar-headless/build.gradle +++ b/briar-headless/build.gradle @@ -29,6 +29,8 @@ dependencies { implementation project(':bramble-core') implementation project(':bramble-java') implementation project(':briar-core') + implementation project(':onionwrapper-core') + implementation project(':onionwrapper-java') linux "org.briarproject:tor-linux:$tor_version" linux "org.briarproject:obfs4proxy-linux:$obfs4proxy_version" diff --git a/onionwrapper b/onionwrapper new file mode 160000 index 000000000..a838a7069 --- /dev/null +++ b/onionwrapper @@ -0,0 +1 @@ +Subproject commit a838a70694e28297829b7567a3fe8c56d1afbf68 diff --git a/settings.gradle b/settings.gradle index f795ed735..05e29ef1f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,6 +6,12 @@ include ':briar-api' include ':briar-core' include ':briar-android' include ':briar-headless' +include ':onionwrapper-core' +include ':onionwrapper-java' +include ':onionwrapper-android' +project (':onionwrapper-core').projectDir = file('onionwrapper/onionwrapper-core') +project (':onionwrapper-java').projectDir = file('onionwrapper/onionwrapper-java') +project (':onionwrapper-android').projectDir = file('onionwrapper/onionwrapper-android') // Enable the mailbox integration tests by passing // `MAILBOX_INTEGRATION_TESTS=true ./gradlew mailbox-integration-tests:test` // on the command line (for CI etc) or set `briar.mailbox_integration_tests=true`