diff --git a/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidResourceProvider.java b/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidResourceProvider.java index 6d391e5ec..89f7f31e3 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidResourceProvider.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidResourceProvider.java @@ -22,9 +22,11 @@ class AndroidResourceProvider implements ResourceProvider { } @Override - public InputStream getResourceInputStream(String name) { + public InputStream getResourceInputStream(String name, String extension) { Resources res = appContext.getResources(); - int resId = res.getIdentifier(name, "raw", appContext.getPackageName()); + // extension is ignored on Android, resources are retrieved without it + int resId = + res.getIdentifier(name, "raw", appContext.getPackageName()); return res.openRawResource(resId); } } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/system/ResourceProvider.java b/bramble-api/src/main/java/org/briarproject/bramble/api/system/ResourceProvider.java index e0bbc63d4..e4bf0b63c 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/system/ResourceProvider.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/system/ResourceProvider.java @@ -7,5 +7,5 @@ import java.io.InputStream; @NotNullByDefault public interface ResourceProvider { - InputStream getResourceInputStream(String name); + InputStream getResourceInputStream(String name, String extension); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java index 8396dd774..57c3b5ca8 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java @@ -1,7 +1,6 @@ package org.briarproject.bramble.plugin.tor; import org.briarproject.bramble.api.lifecycle.IoExecutor; -import org.briarproject.bramble.api.system.ResourceProvider; import java.io.InputStream; import java.util.ArrayList; @@ -24,14 +23,11 @@ class CircumventionProviderImpl implements CircumventionProvider { private static final Set BRIDGES_WORK_IN_COUNTRIES = new HashSet<>(asList(BRIDGES)); - private final ResourceProvider resourceProvider; - @Nullable private volatile List bridges = null; @Inject - CircumventionProviderImpl(ResourceProvider resourceProvider) { - this.resourceProvider = resourceProvider; + CircumventionProviderImpl() { } @Override @@ -50,8 +46,8 @@ class CircumventionProviderImpl implements CircumventionProvider { List bridges = this.bridges; if (bridges != null) return new ArrayList<>(bridges); - InputStream is = - resourceProvider.getResourceInputStream(BRIDGE_FILE_NAME); + InputStream is = getClass().getClassLoader() + .getResourceAsStream(BRIDGE_FILE_NAME); Scanner scanner = new Scanner(is); bridges = new ArrayList<>(); 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 87458806a..f0540ad0c 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 @@ -166,6 +166,12 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void start() throws PluginException { if (used.getAndSet(true)) throw new IllegalStateException(); + if (!torDirectory.exists()) { + if (!torDirectory.mkdirs()) { + LOG.warning("Could not create Tor directory."); + throw new PluginException(); + } + } // Install or update the assets if necessary if (!assetsAreUpToDate()) installAssets(); if (cookieFile.exists() && !cookieFile.delete()) @@ -288,22 +294,23 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private InputStream getTorInputStream() throws IOException { if (LOG.isLoggable(INFO)) LOG.info("Installing Tor binary for " + architecture); - InputStream in = - resourceProvider.getResourceInputStream("tor_" + architecture); + InputStream in = resourceProvider + .getResourceInputStream("tor_" + architecture, ".zip"); ZipInputStream zin = new ZipInputStream(in); if (zin.getNextEntry() == null) throw new IOException(); return zin; } private InputStream getGeoIpInputStream() throws IOException { - InputStream in = resourceProvider.getResourceInputStream("geoip"); + InputStream in = resourceProvider.getResourceInputStream("geoip", + ".zip"); ZipInputStream zin = new ZipInputStream(in); if (zin.getNextEntry() == null) throw new IOException(); return zin; } private InputStream getConfigInputStream() { - return resourceProvider.getResourceInputStream("torrc"); + return getClass().getClassLoader().getResourceAsStream("torrc"); } private void tryToClose(@Nullable Closeable c) { diff --git a/bramble-android/src/main/res/raw/bridges b/bramble-core/src/main/resources/bridges similarity index 100% rename from bramble-android/src/main/res/raw/bridges rename to bramble-core/src/main/resources/bridges diff --git a/bramble-android/src/main/res/raw/torrc b/bramble-core/src/main/resources/torrc similarity index 100% rename from bramble-android/src/main/res/raw/torrc rename to bramble-core/src/main/resources/torrc diff --git a/bramble-j2se/.gitignore b/bramble-j2se/.gitignore index eeb02632a..c980177e1 100644 --- a/bramble-j2se/.gitignore +++ b/bramble-j2se/.gitignore @@ -1,3 +1,4 @@ bin build .settings +src/main/resources/*.zip \ No newline at end of file diff --git a/bramble-j2se/build.gradle b/bramble-j2se/build.gradle index e77258601..4cb436718 100644 --- a/bramble-j2se/build.gradle +++ b/bramble-j2se/build.gradle @@ -7,11 +7,16 @@ apply plugin: 'idea' apply plugin: 'witness' apply from: 'witness.gradle' +configurations { + tor +} + dependencies { implementation project(path: ':bramble-core', configuration: 'default') implementation fileTree(dir: 'libs', include: '*.jar') implementation 'net.java.dev.jna:jna:4.4.0' implementation 'net.java.dev.jna:jna-platform:4.4.0' + tor 'org.briarproject:tor:0.2.9.16@zip' apt 'com.google.dagger:dagger-compiler:2.0.2' @@ -25,6 +30,13 @@ dependencies { testImplementation "org.hamcrest:hamcrest-core:1.3" } +project.afterEvaluate { + copy { + from configurations.tor.collect { zipTree(it) } + into 'src/main/resources' + } +} + tasks.withType(Test) { // Use entropy-gathering device specified on command line, if any systemProperty 'java.security.egd', System.getProperty('java.security.egd') diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/network/JavaNetworkManager.java b/bramble-j2se/src/main/java/org/briarproject/bramble/network/JavaNetworkManager.java new file mode 100644 index 000000000..3eccaebf5 --- /dev/null +++ b/bramble-j2se/src/main/java/org/briarproject/bramble/network/JavaNetworkManager.java @@ -0,0 +1,71 @@ +package org.briarproject.bramble.network; + +import org.briarproject.bramble.api.event.EventBus; +import org.briarproject.bramble.api.lifecycle.Service; +import org.briarproject.bramble.api.network.NetworkManager; +import org.briarproject.bramble.api.network.NetworkStatus; +import org.briarproject.bramble.api.network.event.NetworkStatusEvent; +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; + +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import static java.net.NetworkInterface.getNetworkInterfaces; +import static java.util.Collections.list; +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.util.LogUtils.logException; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +class JavaNetworkManager implements NetworkManager, Service { + + private static final Logger LOG = + Logger.getLogger(JavaNetworkManager.class.getName()); + + private final EventBus eventBus; + + @Inject + JavaNetworkManager(EventBus eventBus) { + this.eventBus = eventBus; + } + + @Override + public void startService() { + eventBus.broadcast(new NetworkStatusEvent(getNetworkStatus())); + } + + @Override + public void stopService() { + } + + @Override + public NetworkStatus getNetworkStatus() { + boolean connected = false; + try { + Enumeration interfaces = getNetworkInterfaces(); + if (interfaces != null) { + for (NetworkInterface i : list(interfaces)) { + if (i.isLoopback()) continue; + if (i.isUp() && i.getInetAddresses().hasMoreElements()) { + if (LOG.isLoggable(INFO)) { + LOG.info("Interface " + i.getDisplayName() + + " is up with at least one address."); + } + connected = true; + break; + } + } + } + } catch (SocketException e) { + logException(LOG, WARNING, e); + } + return new NetworkStatus(connected, false); + } + +} diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/network/JavaNetworkModule.java b/bramble-j2se/src/main/java/org/briarproject/bramble/network/JavaNetworkModule.java new file mode 100644 index 000000000..3c1b1e279 --- /dev/null +++ b/bramble-j2se/src/main/java/org/briarproject/bramble/network/JavaNetworkModule.java @@ -0,0 +1,21 @@ +package org.briarproject.bramble.network; + +import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.api.network.NetworkManager; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +@Module +public class JavaNetworkModule { + + @Provides + @Singleton + NetworkManager provideNetworkManager(LifecycleManager lifecycleManager, + JavaNetworkManager networkManager) { + lifecycleManager.registerService(networkManager); + return networkManager; + } +} diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/tor/LinuxTorPlugin.java b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/tor/LinuxTorPlugin.java new file mode 100644 index 000000000..d9905c1df --- /dev/null +++ b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/tor/LinuxTorPlugin.java @@ -0,0 +1,59 @@ +package org.briarproject.bramble.plugin.tor; + +import org.briarproject.bramble.api.network.NetworkManager; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.Backoff; +import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.bramble.api.system.LocationUtils; +import org.briarproject.bramble.api.system.ResourceProvider; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.CodeSource; +import java.util.concurrent.Executor; + +import javax.net.SocketFactory; + +@NotNullByDefault +class LinuxTorPlugin extends TorPlugin { + + LinuxTorPlugin(Executor ioExecutor, NetworkManager networkManager, + LocationUtils locationUtils, SocketFactory torSocketFactory, + Clock clock, ResourceProvider resourceProvider, + CircumventionProvider circumventionProvider, Backoff backoff, + DuplexPluginCallback callback, String architecture, int maxLatency, + int maxIdleTime, File torDirectory) { + super(ioExecutor, networkManager, locationUtils, torSocketFactory, + clock, resourceProvider, circumventionProvider, backoff, + callback, architecture, maxLatency, maxIdleTime, torDirectory); + } + + @Override + protected int getProcessId() { + try { + // Java 9: ProcessHandle.current().pid() + return Integer.parseInt( + new File("/proc/self").getCanonicalFile().getName()); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @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); + } + } + +} diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/tor/LinuxTorPluginFactory.java b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/tor/LinuxTorPluginFactory.java new file mode 100644 index 000000000..466399a57 --- /dev/null +++ b/bramble-j2se/src/main/java/org/briarproject/bramble/plugin/tor/LinuxTorPluginFactory.java @@ -0,0 +1,103 @@ +package org.briarproject.bramble.plugin.tor; + +import org.briarproject.bramble.api.event.EventBus; +import org.briarproject.bramble.api.network.NetworkManager; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.Backoff; +import org.briarproject.bramble.api.plugin.BackoffFactory; +import org.briarproject.bramble.api.plugin.TorConstants; +import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; +import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; +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.ResourceProvider; + +import java.io.File; +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import javax.annotation.concurrent.Immutable; +import javax.net.SocketFactory; + +import static org.briarproject.bramble.util.OsUtils.isLinux; + +@Immutable +@NotNullByDefault +public class LinuxTorPluginFactory implements DuplexPluginFactory { + + private static final Logger LOG = + Logger.getLogger(LinuxTorPluginFactory.class.getName()); + + private static final int MAX_LATENCY = 30 * 1000; // 30 seconds + private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds + private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute + private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins + private static final double BACKOFF_BASE = 1.2; + + private final Executor ioExecutor; + private final NetworkManager networkManager; + private final LocationUtils locationUtils; + private final EventBus eventBus; + private final SocketFactory torSocketFactory; + private final BackoffFactory backoffFactory; + private final ResourceProvider resourceProvider; + private final CircumventionProvider circumventionProvider; + private final Clock clock; + private final File torDirectory; + + public LinuxTorPluginFactory(Executor ioExecutor, + NetworkManager networkManager, LocationUtils locationUtils, + EventBus eventBus, SocketFactory torSocketFactory, + BackoffFactory backoffFactory, ResourceProvider resourceProvider, + CircumventionProvider circumventionProvider, Clock clock, + File torDirectory) { + this.ioExecutor = ioExecutor; + this.networkManager = networkManager; + this.locationUtils = locationUtils; + this.eventBus = eventBus; + this.torSocketFactory = torSocketFactory; + this.backoffFactory = backoffFactory; + this.resourceProvider = resourceProvider; + this.circumventionProvider = circumventionProvider; + this.clock = clock; + this.torDirectory = torDirectory; + } + + @Override + public TransportId getId() { + return TorConstants.ID; + } + + @Override + public int getMaxLatency() { + return MAX_LATENCY; + } + + @Override + public DuplexPlugin createPlugin(DuplexPluginCallback callback) { + // Check that we have a Tor binary for this architecture + String architecture = null; + if (isLinux()) { + String arch = System.getProperty("os.arch"); + if (arch.equals("amd64")) { + architecture = "linux-x86_64"; + } + } + if (architecture == null) { + LOG.info("Tor is not supported on this architecture"); + return null; + } + + Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, + MAX_POLLING_INTERVAL, BACKOFF_BASE); + LinuxTorPlugin plugin = + new LinuxTorPlugin(ioExecutor, networkManager, locationUtils, + torSocketFactory, clock, resourceProvider, + circumventionProvider, backoff, callback, architecture, + MAX_LATENCY, MAX_IDLE_TIME, torDirectory); + eventBus.addListener(plugin); + return plugin; + } +} diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/system/JavaLocationUtils.java b/bramble-j2se/src/main/java/org/briarproject/bramble/system/JavaLocationUtils.java new file mode 100644 index 000000000..e9aadd8d3 --- /dev/null +++ b/bramble-j2se/src/main/java/org/briarproject/bramble/system/JavaLocationUtils.java @@ -0,0 +1,27 @@ +package org.briarproject.bramble.system; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.system.LocationUtils; + +import java.util.Locale; +import java.util.logging.Logger; + +import javax.inject.Inject; + +@NotNullByDefault +class JavaLocationUtils implements LocationUtils { + + private static final Logger LOG = + Logger.getLogger(JavaLocationUtils.class.getName()); + + @Inject + JavaLocationUtils() { + } + + @Override + public String getCurrentCountry() { + LOG.info("Using user-defined locale"); + return Locale.getDefault().getCountry(); + } + +} diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/system/JavaResourceProvider.java b/bramble-j2se/src/main/java/org/briarproject/bramble/system/JavaResourceProvider.java new file mode 100644 index 000000000..144bff2de --- /dev/null +++ b/bramble-j2se/src/main/java/org/briarproject/bramble/system/JavaResourceProvider.java @@ -0,0 +1,22 @@ +package org.briarproject.bramble.system; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.system.ResourceProvider; + +import java.io.InputStream; + +import javax.inject.Inject; + +@NotNullByDefault +class JavaResourceProvider implements ResourceProvider { + + @Inject + JavaResourceProvider() { + } + + @Override + public InputStream getResourceInputStream(String name, String extension) { + return getClass().getClassLoader() + .getResourceAsStream(name + extension); + } +} diff --git a/bramble-j2se/src/main/java/org/briarproject/bramble/system/JavaSystemModule.java b/bramble-j2se/src/main/java/org/briarproject/bramble/system/JavaSystemModule.java new file mode 100644 index 000000000..c33192d9b --- /dev/null +++ b/bramble-j2se/src/main/java/org/briarproject/bramble/system/JavaSystemModule.java @@ -0,0 +1,25 @@ +package org.briarproject.bramble.system; + +import org.briarproject.bramble.api.system.LocationUtils; +import org.briarproject.bramble.api.system.ResourceProvider; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +@Module +public class JavaSystemModule { + + @Provides + @Singleton + LocationUtils provideLocationUtils(JavaLocationUtils locationUtils) { + return locationUtils; + } + + @Provides + @Singleton + ResourceProvider provideResourceProvider(JavaResourceProvider provider) { + return provider; + } +} diff --git a/bramble-j2se/witness.gradle b/bramble-j2se/witness.gradle index 9fab68416..945bdc09e 100644 --- a/bramble-j2se/witness.gradle +++ b/bramble-j2se/witness.gradle @@ -12,6 +12,7 @@ dependencyVerification { 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8', 'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8', + 'org.briarproject:tor:0.2.9.16:tor-0.2.9.16.zip:f33091ba414d6a952263981d9059b3d0a9093fd277ae887b3cdd02e8f1936558', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',