Compare commits

..

6 Commits

Author SHA1 Message Date
akwizgran
fc9d4dbb66 DO NOT MERGE: Delay sending of attachments. 2020-01-23 08:57:27 -03:00
Torsten Grote
e67e55227b [android] Refactor attachment loading to use LiveData 2020-01-23 08:57:26 -03:00
Torsten Grote
7b106f952d [android] address first round of code review for attachment placeholders 2020-01-23 08:57:26 -03:00
Torsten Grote
e896b1fdd8 [android] support attachments arriving *before* the message containing them 2020-01-23 08:57:25 -03:00
Torsten Grote
bb8e736804 Show placeholders for missing attachments in ImageActivity
and display attachments as they arrive while ImageActivity is open.
2020-01-23 08:57:25 -03:00
Torsten Grote
0e5231955c Refactor attachment loading to support incremental display once loaded 2020-01-23 08:57:25 -03:00
178 changed files with 2497 additions and 4539 deletions

View File

@@ -11,8 +11,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 28
versionCode 10207 versionCode 10205
versionName "1.2.7" versionName "1.2.5"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -38,7 +38,7 @@ configurations {
dependencies { dependencies {
implementation project(path: ':bramble-core', configuration: 'default') implementation project(path: ':bramble-core', configuration: 'default')
tor 'org.briarproject:tor-android:0.3.5.10@zip' tor 'org.briarproject:tor-android:0.3.5.9@zip'
tor 'org.briarproject:obfs4proxy-android:0.0.11-2@zip' tor 'org.briarproject:obfs4proxy-android:0.0.11-2@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24' annotationProcessor 'com.google.dagger:dagger-compiler:2.24'

View File

@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import java.io.File; import java.io.File;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -21,7 +20,6 @@ import javax.annotation.concurrent.GuardedBy;
import javax.inject.Inject; import javax.inject.Inject;
import static android.os.Build.VERSION.SDK_INT; 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.Level.INFO;
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir; import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
import static org.briarproject.bramble.util.LogUtils.logFileOrDir; import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
@@ -32,12 +30,6 @@ class AndroidAccountManager extends AccountManagerImpl
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(AndroidAccountManager.class.getName()); Logger.getLogger(AndroidAccountManager.class.getName());
/**
* Directories that shouldn't be deleted when deleting the user's account.
*/
private static final List<String> PROTECTED_DIR_NAMES =
asList("cache", "code_cache", "lib", "shared_prefs");
protected final Context appContext; protected final Context appContext;
private final SharedPreferences prefs; private final SharedPreferences prefs;
@@ -89,7 +81,7 @@ class AndroidAccountManager extends AccountManagerImpl
if (!prefs.edit().clear().commit()) if (!prefs.edit().clear().commit())
LOG.warning("Could not clear shared preferences"); LOG.warning("Could not clear shared preferences");
} }
// Delete files, except protected directories // Delete files, except lib and shared_prefs directories
Set<File> files = new HashSet<>(); Set<File> files = new HashSet<>();
File dataDir = getDataDir(); File dataDir = getDataDir();
@Nullable @Nullable
@@ -98,12 +90,14 @@ class AndroidAccountManager extends AccountManagerImpl
LOG.warning("Could not list files in app data dir"); LOG.warning("Could not list files in app data dir");
} else { } else {
for (File file : fileArray) { for (File file : fileArray) {
if (!PROTECTED_DIR_NAMES.contains(file.getName())) { String name = file.getName();
if (!name.equals("lib") && !name.equals("shared_prefs")) {
files.add(file); files.add(file);
} }
} }
} }
files.add(appContext.getFilesDir()); files.add(appContext.getFilesDir());
files.add(appContext.getCacheDir());
addIfNotNull(files, appContext.getExternalCacheDir()); addIfNotNull(files, appContext.getExternalCacheDir());
if (SDK_INT >= 19) { if (SDK_INT >= 19) {
for (File file : appContext.getExternalCacheDirs()) { for (File file : appContext.getExternalCacheDirs()) {
@@ -115,16 +109,12 @@ class AndroidAccountManager extends AccountManagerImpl
addIfNotNull(files, file); addIfNotNull(files, file);
} }
} }
// Clear the cache directory but don't delete it
File cacheDir = appContext.getCacheDir();
File[] children = cacheDir.listFiles();
if (children != null) files.addAll(asList(children));
for (File file : files) { for (File file : files) {
if (LOG.isLoggable(INFO)) {
LOG.info("Deleting " + file.getAbsolutePath());
}
deleteFileOrDir(file); deleteFileOrDir(file);
} }
// Recreate the cache dir as some OpenGL drivers expect it to exist
if (!new File(dataDir, "cache").mkdirs())
LOG.warning("Could not recreate cache dir");
} }
private File getDataDir() { private File getDataDir() {

View File

@@ -32,7 +32,6 @@ import static android.content.Intent.ACTION_SCREEN_OFF;
import static android.content.Intent.ACTION_SCREEN_ON; import static android.content.Intent.ACTION_SCREEN_ON;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED; import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.MINUTES;
@@ -77,9 +76,9 @@ class AndroidNetworkManager implements NetworkManager, Service {
filter.addAction(ACTION_SCREEN_ON); filter.addAction(ACTION_SCREEN_ON);
filter.addAction(ACTION_SCREEN_OFF); filter.addAction(ACTION_SCREEN_OFF);
filter.addAction(WIFI_AP_STATE_CHANGED_ACTION); filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED); if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
appContext.registerReceiver(networkStateReceiver, filter); appContext.registerReceiver(networkStateReceiver, filter);
} }
@Override @Override
@@ -137,8 +136,7 @@ class AndroidNetworkManager implements NetworkManager, Service {
} }
private boolean isApEvent(@Nullable String action) { private boolean isApEvent(@Nullable String action) {
return WIFI_AP_STATE_CHANGED_ACTION.equals(action) || return WIFI_AP_STATE_CHANGED_ACTION.equals(action);
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action);
} }
} }
} }

View File

@@ -9,7 +9,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
@@ -25,6 +24,7 @@ import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
@@ -47,10 +47,7 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_OFF; import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON; import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothDevice.ACTION_FOUND; import static android.bluetooth.BluetoothDevice.ACTION_FOUND;
import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_LE;
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE; import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Collections.shuffle;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
@@ -77,12 +74,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
private volatile BluetoothAdapter adapter = null; private volatile BluetoothAdapter adapter = null;
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter, AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor, Executor ioExecutor, Executor ioExecutor, AndroidExecutor androidExecutor,
SecureRandom secureRandom, AndroidExecutor androidExecutor, Context appContext, SecureRandom secureRandom, Clock clock,
Context appContext, Clock clock, Backoff backoff, Backoff backoff, PluginCallback callback, int maxLatency) {
PluginCallback callback, int maxLatency, int maxIdleTime) { super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
super(connectionLimiter, timeoutMonitor, ioExecutor, secureRandom, maxLatency);
backoff, callback, maxLatency, maxIdleTime);
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.appContext = appContext; this.appContext = appContext;
this.clock = clock; this.clock = clock;
@@ -174,10 +170,9 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
return wrapSocket(ss.accept()); return wrapSocket(ss.accept());
} }
private DuplexTransportConnection wrapSocket(BluetoothSocket s) private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
throws IOException { return new AndroidBluetoothTransportConnection(this,
return new AndroidBluetoothTransportConnection(this, connectionLimiter, connectionLimiter, s);
timeoutMonitor, s);
} }
@Override @Override
@@ -245,15 +240,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
break; break;
} else if (ACTION_FOUND.equals(action)) { } else if (ACTION_FOUND.equals(action)) {
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE); BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
// Ignore Bluetooth LE devices String address = d.getAddress();
if (SDK_INT < 18 || d.getType() != DEVICE_TYPE_LE) { if (LOG.isLoggable(INFO))
String address = d.getAddress(); LOG.info("Discovered " + scrubMacAddress(address));
if (LOG.isLoggable(INFO)) if (!addresses.contains(address))
LOG.info("Discovered " + addresses.add(address);
scrubMacAddress(address));
if (!addresses.contains(address))
addresses.add(address);
}
} }
now = clock.currentTimeMillis(); now = clock.currentTimeMillis();
} }
@@ -269,7 +260,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
appContext.unregisterReceiver(receiver); appContext.unregisterReceiver(receiver);
} }
// Shuffle the addresses so we don't always try the same one first // Shuffle the addresses so we don't always try the same one first
shuffle(addresses); Collections.shuffle(addresses);
return addresses; return addresses;
} }

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.bluetooth;
import android.content.Context; import android.content.Context;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -26,7 +25,6 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
public class AndroidBluetoothPluginFactory implements DuplexPluginFactory { public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds 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 MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
@@ -37,20 +35,18 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final EventBus eventBus; private final EventBus eventBus;
private final Clock clock; private final Clock clock;
private final TimeoutMonitor timeoutMonitor;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
public AndroidBluetoothPluginFactory(Executor ioExecutor, public AndroidBluetoothPluginFactory(Executor ioExecutor,
AndroidExecutor androidExecutor, Context appContext, AndroidExecutor androidExecutor, Context appContext,
SecureRandom secureRandom, EventBus eventBus, Clock clock, SecureRandom secureRandom, EventBus eventBus, Clock clock,
TimeoutMonitor timeoutMonitor, BackoffFactory backoffFactory) { BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.appContext = appContext; this.appContext = appContext;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.eventBus = eventBus; this.eventBus = eventBus;
this.clock = clock; this.clock = clock;
this.timeoutMonitor = timeoutMonitor;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
} }
@@ -71,9 +67,8 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin( AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionLimiter, timeoutMonitor, ioExecutor, secureRandom, connectionLimiter, ioExecutor, androidExecutor, appContext,
androidExecutor, appContext, clock, backoff, secureRandom, clock, backoff, callback, MAX_LATENCY);
callback, MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.plugin.bluetooth;
import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
@@ -11,33 +10,24 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.util.AndroidUtils.isValidBluetoothAddress;
@NotNullByDefault @NotNullByDefault
class AndroidBluetoothTransportConnection class AndroidBluetoothTransportConnection
extends AbstractDuplexTransportConnection { extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionLimiter; private final BluetoothConnectionLimiter connectionManager;
private final BluetoothSocket socket; private final BluetoothSocket socket;
private final InputStream in;
AndroidBluetoothTransportConnection(Plugin plugin, AndroidBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionLimiter, BluetoothConnectionLimiter connectionManager,
TimeoutMonitor timeoutMonitor, BluetoothSocket socket) BluetoothSocket socket) {
throws IOException {
super(plugin); super(plugin);
this.connectionLimiter = connectionLimiter; this.connectionManager = connectionManager;
this.socket = socket; this.socket = socket;
in = timeoutMonitor.createTimeoutInputStream(
socket.getInputStream(), plugin.getMaxIdleTime() * 2);
String address = socket.getRemoteDevice().getAddress();
if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address);
} }
@Override @Override
protected InputStream getInputStream() { protected InputStream getInputStream() throws IOException {
return in; return socket.getInputStream();
} }
@Override @Override
@@ -50,7 +40,7 @@ class AndroidBluetoothTransportConnection
try { try {
socket.close(); socket.close();
} finally { } finally {
connectionLimiter.connectionClosed(this); connectionManager.connectionClosed(this);
} }
} }
} }

View File

@@ -19,7 +19,7 @@ import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.List; import java.util.Collection;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -40,6 +40,19 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
private static final Logger LOG = private static final Logger LOG =
getLogger(AndroidLanTcpPlugin.class.getName()); getLogger(AndroidLanTcpPlugin.class.getName());
private static final byte[] WIFI_AP_ADDRESS_BYTES =
{(byte) 192, (byte) 168, 43, 1};
private static final InetAddress WIFI_AP_ADDRESS;
static {
try {
WIFI_AP_ADDRESS = InetAddress.getByAddress(WIFI_AP_ADDRESS_BYTES);
} catch (UnknownHostException e) {
// Should only be thrown if the address has an illegal length
throw new AssertionError(e);
}
}
private final Executor connectionStatusExecutor; private final Executor connectionStatusExecutor;
private final ConnectivityManager connectivityManager; private final ConnectivityManager connectivityManager;
@Nullable @Nullable
@@ -49,9 +62,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
AndroidLanTcpPlugin(Executor ioExecutor, Context appContext, AndroidLanTcpPlugin(Executor ioExecutor, Context appContext,
Backoff backoff, PluginCallback callback, int maxLatency, Backoff backoff, PluginCallback callback, int maxLatency,
int maxIdleTime, int connectionTimeout) { int maxIdleTime) {
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime, super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
connectionTimeout);
// Don't execute more than one connection status check at a time // Don't execute more than one connection status check at a time
connectionStatusExecutor = connectionStatusExecutor =
new PoliteExecutor("AndroidLanTcpPlugin", ioExecutor, 1); new PoliteExecutor("AndroidLanTcpPlugin", ioExecutor, 1);
@@ -67,7 +79,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
@Override @Override
public void start() { public void start() {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
initialisePortProperty();
running = true; running = true;
updateConnectionStatus(); updateConnectionStatus();
} }
@@ -84,19 +95,16 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
} }
@Override @Override
protected List<InetAddress> getUsableLocalInetAddresses() { protected Collection<InetAddress> getLocalIpAddresses() {
// If the device doesn't have wifi, don't open any sockets // If the device doesn't have wifi, don't open any sockets
if (wifiManager == null) return emptyList(); if (wifiManager == null) return emptyList();
// If we're connected to a wifi network, return its address // If we're connected to a wifi network, use that network
WifiInfo info = wifiManager.getConnectionInfo(); WifiInfo info = wifiManager.getConnectionInfo();
if (info != null && info.getIpAddress() != 0) { if (info != null && info.getIpAddress() != 0)
return singletonList(intToInetAddress(info.getIpAddress())); return singletonList(intToInetAddress(info.getIpAddress()));
}
// If we're running an access point, return its address // If we're running an access point, return its address
for (InetAddress addr : getLocalInetAddresses()) { if (super.getLocalIpAddresses().contains(WIFI_AP_ADDRESS))
if (addr.equals(WIFI_AP_ADDRESS)) return singletonList(addr); return singletonList(WIFI_AP_ADDRESS);
if (addr.equals(WIFI_DIRECT_AP_ADDRESS)) return singletonList(addr);
}
// No suitable addresses // No suitable addresses
return emptyList(); return emptyList();
} }
@@ -136,9 +144,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
private void updateConnectionStatus() { private void updateConnectionStatus() {
connectionStatusExecutor.execute(() -> { connectionStatusExecutor.execute(() -> {
if (!running) return; if (!running) return;
List<InetAddress> addrs = getUsableLocalInetAddresses(); Collection<InetAddress> addrs = getLocalIpAddresses();
if (addrs.contains(WIFI_AP_ADDRESS) if (addrs.contains(WIFI_AP_ADDRESS)) {
|| addrs.contains(WIFI_DIRECT_AP_ADDRESS)) {
LOG.info("Providing wifi hotspot"); LOG.info("Providing wifi hotspot");
// There's no corresponding Network object and thus no way // There's no corresponding Network object and thus no way
// to get a suitable socket factory, so we won't be able to // to get a suitable socket factory, so we won't be able to

View File

@@ -21,11 +21,10 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
@NotNullByDefault @NotNullByDefault
public class AndroidLanTcpPluginFactory implements DuplexPluginFactory { public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30_000; // 30 seconds private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MAX_IDLE_TIME = 30_000; // 30 seconds private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
private static final int CONNECTION_TIMEOUT = 3_000; // 3 seconds private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
private static final int MIN_POLLING_INTERVAL = 60_000; // 1 minute private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor; private final Executor ioExecutor;
@@ -56,8 +55,7 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor, AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor,
appContext, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME, appContext, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
CONNECTION_TIMEOUT);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -71,7 +71,7 @@ public class AndroidUtils {
return new Pair<>("", ""); return new Pair<>("", "");
} }
public static boolean isValidBluetoothAddress(@Nullable String address) { private static boolean isValidBluetoothAddress(@Nullable String address) {
return !StringUtils.isNullOrEmpty(address) return !StringUtils.isNullOrEmpty(address)
&& BluetoothAdapter.checkBluetoothAddress(address) && BluetoothAdapter.checkBluetoothAddress(address)
&& !address.equals(FAKE_BLUETOOTH_ADDRESS); && !address.equals(FAKE_BLUETOOTH_ADDRESS);

View File

@@ -72,9 +72,7 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
@Test @Test
public void testDeleteAccountClearsSharedPrefsAndDeletesFiles() public void testDeleteAccountClearsSharedPrefsAndDeletesFiles()
throws Exception { throws Exception {
// Directories 'code_cache', 'lib' and 'shared_prefs' should be spared // Directories 'lib' and 'shared_prefs' should be spared
File codeCacheDir = new File(testDir, "code_cache");
File codeCacheFile = new File(codeCacheDir, "file");
File libDir = new File(testDir, "lib"); File libDir = new File(testDir, "lib");
File libFile = new File(libDir, "file"); File libFile = new File(libDir, "file");
File sharedPrefsDir = new File(testDir, "shared_prefs"); File sharedPrefsDir = new File(testDir, "shared_prefs");
@@ -113,8 +111,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertTrue(dbDir.mkdirs()); assertTrue(dbDir.mkdirs());
assertTrue(keyDir.mkdirs()); assertTrue(keyDir.mkdirs());
assertTrue(codeCacheDir.mkdirs());
assertTrue(codeCacheFile.createNewFile());
assertTrue(libDir.mkdirs()); assertTrue(libDir.mkdirs());
assertTrue(libFile.createNewFile()); assertTrue(libFile.createNewFile());
assertTrue(sharedPrefsDir.mkdirs()); assertTrue(sharedPrefsDir.mkdirs());
@@ -130,8 +126,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertFalse(dbDir.exists()); assertFalse(dbDir.exists());
assertFalse(keyDir.exists()); assertFalse(keyDir.exists());
assertTrue(codeCacheDir.exists());
assertTrue(codeCacheFile.exists());
assertTrue(libDir.exists()); assertTrue(libDir.exists());
assertTrue(libFile.exists()); assertTrue(libFile.exists());
assertTrue(sharedPrefsDir.exists()); assertTrue(sharedPrefsDir.exists());

View File

@@ -70,7 +70,7 @@ dependencyVerification {
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca', 'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349', 'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
'org.briarproject:obfs4proxy-android:0.0.11-2:obfs4proxy-android-0.0.11-2.zip:57e55cbe87aa2aac210fdbb6cd8cdeafe15f825406a08ebf77a8b787aa2c6a8a', 'org.briarproject:obfs4proxy-android:0.0.11-2:obfs4proxy-android-0.0.11-2.zip:57e55cbe87aa2aac210fdbb6cd8cdeafe15f825406a08ebf77a8b787aa2c6a8a',
'org.briarproject:tor-android:0.3.5.10:tor-android-0.3.5.10.zip:edd83bf557fcff2105eaa0bdb3f607a6852ebe7360920929ae3039dd5f4774c5', 'org.briarproject:tor-android:0.3.5.9:tor-android-0.3.5.9.zip:853b0440feccd6904bd03e6b2de53a62ebcde1d58068beeadc447a7dff950bc8',
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a', 'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
'org.codehaus.groovy:groovy-all:2.4.15:groovy-all-2.4.15.jar:51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0', 'org.codehaus.groovy:groovy-all:2.4.15:groovy-all-2.4.15.jar:51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0',

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.api.account; package org.briarproject.bramble.api.account;
import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -14,8 +13,7 @@ public interface AccountManager {
* Returns true if the manager has the database key. This will be false * Returns true if the manager has the database key. This will be false
* before {@link #createAccount(String, String)} or {@link #signIn(String)} * before {@link #createAccount(String, String)} or {@link #signIn(String)}
* has been called, and true after {@link #createAccount(String, String)} * has been called, and true after {@link #createAccount(String, String)}
* or {@link #signIn(String)} has returned true, until * or {@link #signIn(String)} has returned true, until the process exits.
* {@link #deleteAccount()} is called or the process exits.
*/ */
boolean hasDatabaseKey(); boolean hasDatabaseKey();
@@ -24,22 +22,25 @@ public interface AccountManager {
* before {@link #createAccount(String, String)} or {@link #signIn(String)} * before {@link #createAccount(String, String)} or {@link #signIn(String)}
* has been called, and non-null after * has been called, and non-null after
* {@link #createAccount(String, String)} or {@link #signIn(String)} has * {@link #createAccount(String, String)} or {@link #signIn(String)} has
* returned true, until {@link #deleteAccount()} is called or the process * returned true, until the process exits.
* exits.
*/ */
@Nullable @Nullable
SecretKey getDatabaseKey(); SecretKey getDatabaseKey();
/** /**
* Returns true if the encrypted database key can be loaded from disk. * Returns true if the encrypted database key can be loaded from disk, and
* the database directory exists and is a directory.
*/ */
boolean accountExists(); boolean accountExists();
/** /**
* Creates an identity with the given name and registers it with the * Creates an identity with the given name and registers it with the
* {@link IdentityManager}. Creates a database key, encrypts it with the * {@link IdentityManager}. Creates a database key, encrypts it with the
* given password and stores it on disk. {@link #accountExists()} will * given password and stores it on disk.
* return true after this method returns true. * <p/>
* This method does not create the database directory, so
* {@link #accountExists()} will continue to return false until the
* database directory is created.
*/ */
boolean createAccount(String name, String password); boolean createAccount(String name, String password);
@@ -53,19 +54,17 @@ public interface AccountManager {
* Loads the encrypted database key from disk and decrypts it with the * Loads the encrypted database key from disk and decrypts it with the
* given password. * given password.
* *
* @throws DecryptionException If the database key could not be loaded and * @return true if the database key was successfully loaded and decrypted.
* decrypted.
*/ */
void signIn(String password) throws DecryptionException; boolean signIn(String password);
/** /**
* Loads the encrypted database key from disk, decrypts it with the old * Loads the encrypted database key from disk, decrypts it with the old
* password, encrypts it with the new password, and stores it on disk, * password, encrypts it with the new password, and stores it on disk,
* replacing the old key. * replacing the old key.
* *
* @throws DecryptionException If the database key could not be loaded and * @return true if the database key was successfully loaded, re-encrypted
* decrypted. * and stored.
*/ */
void changePassword(String oldPassword, String newPassword) boolean changePassword(String oldPassword, String newPassword);
throws DecryptionException;
} }

View File

@@ -1,19 +0,0 @@
package org.briarproject.bramble.api.connection;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* A duplex sync connection that can be closed by interrupting its outgoing
* sync session.
*/
@NotNullByDefault
public interface InterruptibleConnection {
/**
* Interrupts the connection's outgoing sync session. If the underlying
* transport connection is alive and the remote peer is cooperative, this
* should result in both sync sessions ending and the connection being
* cleanly closed.
*/
void interruptOutgoingSession();
}

View File

@@ -142,17 +142,16 @@ public interface CryptoComponent {
/** /**
* Decrypts and authenticates the given ciphertext that has been read from * Decrypts and authenticates the given ciphertext that has been read from
* storage. The encryption and authentication keys are derived from the * storage. The encryption and authentication keys are derived from the
* given password. * given password. Returns null if the ciphertext cannot be decrypted and
* authenticated (for example, if the password is wrong).
* *
* @param keyStrengthener Used to strengthen the password-based key. If * @param keyStrengthener Used to strengthen the password-based key. If
* null, or if strengthening was not used when encrypting the ciphertext, * null, or if strengthening was not used when encrypting the ciphertext,
* the password-based key will not be strengthened * the password-based key will not be strengthened
* @throws DecryptionException If the ciphertext cannot be decrypted and
* authenticated (for example, if the password is wrong).
*/ */
@Nullable
byte[] decryptWithPassword(byte[] ciphertext, String password, byte[] decryptWithPassword(byte[] ciphertext, String password,
@Nullable KeyStrengthener keyStrengthener) @Nullable KeyStrengthener keyStrengthener);
throws DecryptionException;
/** /**
* Returns true if the given ciphertext was encrypted using a strengthened * Returns true if the given ciphertext was encrypted using a strengthened

View File

@@ -1,17 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public class DecryptionException extends Exception {
private final DecryptionResult result;
public DecryptionException(DecryptionResult result) {
this.result = result;
}
public DecryptionResult getDecryptionResult() {
return result;
}
}

View File

@@ -1,29 +0,0 @@
package org.briarproject.bramble.api.crypto;
/**
* The result of a password-based decryption operation.
*/
public enum DecryptionResult {
/**
* Decryption succeeded.
*/
SUCCESS,
/**
* Decryption failed because the format of the ciphertext was invalid.
*/
INVALID_CIPHERTEXT,
/**
* Decryption failed because the {@link KeyStrengthener} used for
* encryption was not available for decryption.
*/
KEY_STRENGTHENER_ERROR,
/**
* Decryption failed because the password used for decryption did not match
* the password used for encryption.
*/
INVALID_PASSWORD
}

View File

@@ -1,15 +0,0 @@
package org.briarproject.bramble.api.io;
import java.io.InputStream;
public interface TimeoutMonitor {
/**
* Returns an {@link InputStream} that wraps the given stream and allows
* read timeouts to be detected.
*
* @param timeoutMs The read timeout in milliseconds. Timeouts will be
* detected eventually but are not guaranteed to be detected immediately.
*/
InputStream createTimeoutInputStream(InputStream in, long timeoutMs);
}

View File

@@ -1,11 +1,8 @@
package org.briarproject.bramble.api.connection; package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@NotNullByDefault @NotNullByDefault

View File

@@ -1,17 +1,14 @@
package org.briarproject.bramble.api.connection; package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent; import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.sync.Priority;
import java.util.Collection; import java.util.Collection;
@@ -23,57 +20,25 @@ public interface ConnectionRegistry {
/** /**
* Registers a connection with the given contact over the given transport. * Registers a connection with the given contact over the given transport.
* <p>
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts * Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
* {@link ContactConnectedEvent} if this is the only connection with the * {@link ContactConnectedEvent} if this is the only connection with the
* contact. * contact.
*/ */
void registerConnection(ContactId c, TransportId t, void registerConnection(ContactId c, TransportId t, boolean incoming);
InterruptibleConnection conn, boolean incoming);
/** /**
* Unregisters a connection with the given contact over the given transport. * Unregisters a connection with the given contact over the given transport.
* <p>
* Broadcasts {@link ConnectionClosedEvent}. Also broadcasts * Broadcasts {@link ConnectionClosedEvent}. Also broadcasts
* {@link ContactDisconnectedEvent} if this is the only connection with * {@link ContactDisconnectedEvent} if this is the only connection with
* the contact. * the contact.
*/ */
void unregisterConnection(ContactId c, TransportId t, void unregisterConnection(ContactId c, TransportId t, boolean incoming);
InterruptibleConnection conn, boolean incoming, boolean exception);
/**
* Sets the {@link Priority priority} of a connection that was previously
* registered via {@link #registerConnection(ContactId, TransportId,
* InterruptibleConnection, boolean)}.
* <p>
* If the registry has any "better" connections with the given contact, the
* given connection will be interrupted. If the registry has any "worse"
* connections with the given contact, those connections will be
* interrupted.
* <p>
* Connection A is considered "better" than connection B if both
* connections have had their priorities set, and either A's transport is
* {@link PluginConfig#getTransportPreferences() preferred} to B's, or
* they use the same transport and A has higher {@link Priority priority}
* than B.
* <p>
* For backward compatibility, connections without priorities are not
* considered better or worse than other connections.
*/
void setPriority(ContactId c, TransportId t, InterruptibleConnection conn,
Priority priority);
/** /**
* Returns any contacts that are connected via the given transport. * Returns any contacts that are connected via the given transport.
*/ */
Collection<ContactId> getConnectedContacts(TransportId t); Collection<ContactId> getConnectedContacts(TransportId t);
/**
* Returns any contacts that are connected via the given transport or any
* {@link PluginConfig#getTransportPreferences() better} transport.
*/
Collection<ContactId> getConnectedOrBetterContacts(TransportId t);
/** /**
* Returns true if the given contact is connected via the given transport. * Returns true if the given contact is connected via the given transport.
*/ */

View File

@@ -4,10 +4,10 @@ public interface LanTcpConstants {
TransportId ID = new TransportId("org.briarproject.bramble.lan"); TransportId ID = new TransportId("org.briarproject.bramble.lan");
// Transport properties (shared with contacts) // a transport property (shared with contacts)
String PROP_IP_PORTS = "ipPorts"; String PROP_IP_PORTS = "ipPorts";
String PROP_PORT = "port";
// A local setting // a local setting
String PREF_LAN_IP_PORTS = "ipPorts"; String PREF_LAN_IP_PORTS = "ipPorts";
} }

View File

@@ -5,8 +5,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map;
@NotNullByDefault @NotNullByDefault
public interface PluginConfig { public interface PluginConfig {
@@ -16,11 +14,4 @@ public interface PluginConfig {
Collection<SimplexPluginFactory> getSimplexFactories(); Collection<SimplexPluginFactory> getSimplexFactories();
boolean shouldPoll(); boolean shouldPoll();
/**
* Returns a map representing transport preferences. For each entry in the
* map, connections via the transports identified by the value are
* preferred to connections via the transport identified by the key.
*/
Map<TransportId, List<TransportId>> getTransportPreferences();
} }

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -15,8 +14,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
public abstract class AbstractDuplexTransportConnection public abstract class AbstractDuplexTransportConnection
implements DuplexTransportConnection { implements DuplexTransportConnection {
protected final TransportProperties remote = new TransportProperties();
private final Plugin plugin; private final Plugin plugin;
private final Reader reader; private final Reader reader;
private final Writer writer; private final Writer writer;
@@ -47,11 +44,6 @@ public abstract class AbstractDuplexTransportConnection
return writer; return writer;
} }
@Override
public TransportProperties getRemoteProperties() {
return remote;
}
private class Reader implements TransportConnectionReader { private class Reader implements TransportConnectionReader {
@Override @Override

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.plugin.duplex;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.properties.TransportProperties;
/** /**
* An interface for reading and writing data over a duplex transport. The * An interface for reading and writing data over a duplex transport. The
@@ -24,10 +23,4 @@ public interface DuplexTransportConnection {
* for writing to the connection. * for writing to the connection.
*/ */
TransportConnectionWriter getWriter(); TransportConnectionWriter getWriter();
/**
* Returns a possibly empty set of {@link TransportProperties} describing
* the remote peer.
*/
TransportProperties getRemoteProperties();
} }

View File

@@ -13,14 +13,13 @@ public class ConnectionClosedEvent extends Event {
private final ContactId contactId; private final ContactId contactId;
private final TransportId transportId; private final TransportId transportId;
private final boolean incoming, exception; private final boolean incoming;
public ConnectionClosedEvent(ContactId contactId, TransportId transportId, public ConnectionClosedEvent(ContactId contactId, TransportId transportId,
boolean incoming, boolean exception) { boolean incoming) {
this.contactId = contactId; this.contactId = contactId;
this.transportId = transportId; this.transportId = transportId;
this.incoming = incoming; this.incoming = incoming;
this.exception = exception;
} }
public ContactId getContactId() { public ContactId getContactId() {
@@ -34,8 +33,4 @@ public class ConnectionClosedEvent extends Event {
public boolean isIncoming() { public boolean isIncoming() {
return incoming; return incoming;
} }
public boolean isException() {
return exception;
}
} }

View File

@@ -11,28 +11,4 @@ public interface TransportPropertyConstants {
* The maximum length of a property's key or value in UTF-8 bytes. * The maximum length of a property's key or value in UTF-8 bytes.
*/ */
int MAX_PROPERTY_LENGTH = 100; int MAX_PROPERTY_LENGTH = 100;
/**
* Message metadata key for the transport ID of a local or remote update,
* as a BDF string.
*/
String MSG_KEY_TRANSPORT_ID = "transportId";
/**
* Message metadata key for the version number of a local or remote update,
* as a BDF long.
*/
String MSG_KEY_VERSION = "version";
/**
* Message metadata key for whether an update is local or remote, as a BDF
* boolean.
*/
String MSG_KEY_LOCAL = "local";
/**
* Group metadata key for any discovered transport properties of the
* contact, as a BDF dictionary.
*/
String GROUP_KEY_DISCOVERED = "discovered";
} }

View File

@@ -34,14 +34,6 @@ public interface TransportPropertyManager {
void addRemoteProperties(Transaction txn, ContactId c, void addRemoteProperties(Transaction txn, ContactId c,
Map<TransportId, TransportProperties> props) throws DbException; Map<TransportId, TransportProperties> props) throws DbException;
/**
* Stores the given properties discovered from an incoming transport
* connection. They will be overridden by any properties received while
* adding the contact or synced from the contact.
*/
void addRemotePropertiesFromConnection(ContactId c, TransportId t,
TransportProperties props) throws DbException;
/** /**
* Returns the local transport properties for all transports. * Returns the local transport properties for all transports.
*/ */

View File

@@ -1,23 +0,0 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* A record containing a nonce for choosing between redundant sessions.
*/
@Immutable
@NotNullByDefault
public class Priority {
private final byte[] nonce;
public Priority(byte[] nonce) {
this.nonce = nonce;
}
public byte[] getNonce() {
return nonce;
}
}

View File

@@ -1,13 +0,0 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* An interface for handling a {@link Priority} record received by an
* incoming {@link SyncSession}.
*/
@NotNullByDefault
public interface PriorityHandler {
void handle(Priority p);
}

View File

@@ -10,5 +10,4 @@ public interface RecordTypes {
byte OFFER = 2; byte OFFER = 2;
byte REQUEST = 3; byte REQUEST = 3;
byte VERSIONS = 4; byte VERSIONS = 4;
byte PRIORITY = 5;
} }

View File

@@ -49,10 +49,4 @@ public interface SyncConstants {
* simultaneously. * simultaneously.
*/ */
int MAX_SUPPORTED_VERSIONS = 10; int MAX_SUPPORTED_VERSIONS = 10;
/**
* The length of the priority nonce used for choosing between redundant
* connections.
*/
int PRIORITY_NONCE_BYTES = 16;
} }

View File

@@ -28,8 +28,4 @@ public interface SyncRecordReader {
boolean hasVersions() throws IOException; boolean hasVersions() throws IOException;
Versions readVersions() throws IOException; Versions readVersions() throws IOException;
boolean hasPriority() throws IOException;
Priority readPriority() throws IOException;
} }

View File

@@ -17,7 +17,5 @@ public interface SyncRecordWriter {
void writeVersions(Versions v) throws IOException; void writeVersions(Versions v) throws IOException;
void writePriority(Priority p) throws IOException;
void flush() throws IOException; void flush() throws IOException;
} }

View File

@@ -6,18 +6,14 @@ import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.InputStream; import java.io.InputStream;
import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
public interface SyncSessionFactory { public interface SyncSessionFactory {
SyncSession createIncomingSession(ContactId c, InputStream in, SyncSession createIncomingSession(ContactId c, InputStream in);
PriorityHandler handler);
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency, SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
StreamWriter streamWriter); StreamWriter streamWriter);
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency, SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
int maxIdleTime, StreamWriter streamWriter, int maxIdleTime, StreamWriter streamWriter);
@Nullable Priority priority);
} }

View File

@@ -117,10 +117,4 @@ public class IoUtils {
throw new IOException(e); throw new IOException(e);
} }
} }
public static boolean isNonEmptyDirectory(File f) {
if (!f.isDirectory()) return false;
File[] children = f.listFiles();
return children != null && children.length > 0;
}
} }

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble; package org.briarproject.bramble;
import org.briarproject.bramble.client.ClientModule; import org.briarproject.bramble.client.ClientModule;
import org.briarproject.bramble.connection.ConnectionModule;
import org.briarproject.bramble.contact.ContactModule; import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule; import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule; import org.briarproject.bramble.crypto.CryptoModule;
@@ -10,7 +9,6 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
import org.briarproject.bramble.db.DatabaseModule; import org.briarproject.bramble.db.DatabaseModule;
import org.briarproject.bramble.event.EventModule; import org.briarproject.bramble.event.EventModule;
import org.briarproject.bramble.identity.IdentityModule; import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.io.IoModule;
import org.briarproject.bramble.keyagreement.KeyAgreementModule; import org.briarproject.bramble.keyagreement.KeyAgreementModule;
import org.briarproject.bramble.lifecycle.LifecycleModule; import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule; import org.briarproject.bramble.plugin.PluginModule;
@@ -29,7 +27,6 @@ import dagger.Module;
@Module(includes = { @Module(includes = {
ClientModule.class, ClientModule.class,
ConnectionModule.class,
ContactModule.class, ContactModule.class,
CryptoModule.class, CryptoModule.class,
CryptoExecutorModule.class, CryptoExecutorModule.class,
@@ -38,7 +35,6 @@ import dagger.Module;
DatabaseExecutorModule.class, DatabaseExecutorModule.class,
EventModule.class, EventModule.class,
IdentityModule.class, IdentityModule.class,
IoModule.class,
KeyAgreementModule.class, KeyAgreementModule.class,
LifecycleModule.class, LifecycleModule.class,
PluginModule.class, PluginModule.class,

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.account;
import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.KeyStrengthener; import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
@@ -18,7 +17,6 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -26,7 +24,6 @@ import javax.annotation.concurrent.GuardedBy;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.fromHexString; import static org.briarproject.bramble.util.StringUtils.fromHexString;
import static org.briarproject.bramble.util.StringUtils.toHexString; import static org.briarproject.bramble.util.StringUtils.toHexString;
@@ -98,7 +95,7 @@ class AccountManagerImpl implements AccountManager {
} }
try { try {
BufferedReader reader = new BufferedReader(new InputStreamReader( BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(f), Charset.forName("UTF-8"))); new FileInputStream(f), "UTF-8"));
String key = reader.readLine(); String key = reader.readLine();
reader.close(); reader.close();
return key; return key;
@@ -150,7 +147,7 @@ class AccountManagerImpl implements AccountManager {
@GuardedBy("stateChangeLock") @GuardedBy("stateChangeLock")
private void writeDbKeyToFile(String key, File f) throws IOException { private void writeDbKeyToFile(String key, File f) throws IOException {
FileOutputStream out = new FileOutputStream(f); FileOutputStream out = new FileOutputStream(f);
out.write(key.getBytes(Charset.forName("UTF-8"))); out.write(key.getBytes("UTF-8"));
out.flush(); out.flush();
out.close(); out.close();
} }
@@ -158,7 +155,8 @@ class AccountManagerImpl implements AccountManager {
@Override @Override
public boolean accountExists() { public boolean accountExists() {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
return loadEncryptedDatabaseKey() != null; return loadEncryptedDatabaseKey() != null
&& databaseConfig.getDatabaseDirectory().isDirectory();
} }
} }
@@ -195,24 +193,31 @@ class AccountManagerImpl implements AccountManager {
} }
@Override @Override
public void signIn(String password) throws DecryptionException { public boolean signIn(String password) {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
databaseKey = loadAndDecryptDatabaseKey(password); SecretKey key = loadAndDecryptDatabaseKey(password);
if (key == null) return false;
databaseKey = key;
return true;
} }
} }
@GuardedBy("stateChangeLock") @GuardedBy("stateChangeLock")
private SecretKey loadAndDecryptDatabaseKey(String password) @Nullable
throws DecryptionException { private SecretKey loadAndDecryptDatabaseKey(String password) {
String hex = loadEncryptedDatabaseKey(); String hex = loadEncryptedDatabaseKey();
if (hex == null) { if (hex == null) {
LOG.warning("Failed to load encrypted database key"); LOG.warning("Failed to load encrypted database key");
throw new DecryptionException(INVALID_CIPHERTEXT); return null;
} }
byte[] ciphertext = fromHexString(hex); byte[] ciphertext = fromHexString(hex);
KeyStrengthener keyStrengthener = databaseConfig.getKeyStrengthener(); KeyStrengthener keyStrengthener = databaseConfig.getKeyStrengthener();
byte[] plaintext = crypto.decryptWithPassword(ciphertext, password, byte[] plaintext = crypto.decryptWithPassword(ciphertext, password,
keyStrengthener); keyStrengthener);
if (plaintext == null) {
LOG.info("Failed to decrypt database key");
return null;
}
SecretKey key = new SecretKey(plaintext); SecretKey key = new SecretKey(plaintext);
// If the DB key was encrypted with a weak key and a key strengthener // If the DB key was encrypted with a weak key and a key strengthener
// is now available, re-encrypt the DB key with a strengthened key // is now available, re-encrypt the DB key with a strengthened key
@@ -225,11 +230,10 @@ class AccountManagerImpl implements AccountManager {
} }
@Override @Override
public void changePassword(String oldPassword, String newPassword) public boolean changePassword(String oldPassword, String newPassword) {
throws DecryptionException {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
SecretKey key = loadAndDecryptDatabaseKey(oldPassword); SecretKey key = loadAndDecryptDatabaseKey(oldPassword);
encryptAndStoreDatabaseKey(key, newPassword); return key != null && encryptAndStoreDatabaseKey(key, newPassword);
} }
} }
} }

View File

@@ -1,79 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.IoUtils.read;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
abstract class Connection {
protected static final Logger LOG = getLogger(Connection.class.getName());
final KeyManager keyManager;
final ConnectionRegistry connectionRegistry;
final StreamReaderFactory streamReaderFactory;
final StreamWriterFactory streamWriterFactory;
Connection(KeyManager keyManager, ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) {
this.keyManager = keyManager;
this.connectionRegistry = connectionRegistry;
this.streamReaderFactory = streamReaderFactory;
this.streamWriterFactory = streamWriterFactory;
}
@Nullable
StreamContext recogniseTag(TransportConnectionReader reader,
TransportId transportId) {
StreamContext ctx;
try {
byte[] tag = readTag(reader.getInputStream());
return keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
return null;
}
}
private byte[] readTag(InputStream in) throws IOException {
byte[] tag = new byte[TAG_LENGTH];
read(in, tag);
return tag;
}
void disposeOnError(TransportConnectionReader reader, boolean recognised) {
try {
reader.dispose(true, recognised);
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
void disposeOnError(TransportConnectionWriter writer) {
try {
writer.dispose(true);
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -1,114 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable
@NotNullByDefault
class ConnectionManagerImpl implements ConnectionManager {
private final Executor ioExecutor;
private final KeyManager keyManager;
private final StreamReaderFactory streamReaderFactory;
private final StreamWriterFactory streamWriterFactory;
private final SyncSessionFactory syncSessionFactory;
private final HandshakeManager handshakeManager;
private final ContactExchangeManager contactExchangeManager;
private final ConnectionRegistry connectionRegistry;
private final TransportPropertyManager transportPropertyManager;
private final SecureRandom secureRandom;
@Inject
ConnectionManagerImpl(@IoExecutor Executor ioExecutor,
KeyManager keyManager, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
HandshakeManager handshakeManager,
ContactExchangeManager contactExchangeManager,
ConnectionRegistry connectionRegistry,
TransportPropertyManager transportPropertyManager,
SecureRandom secureRandom) {
this.ioExecutor = ioExecutor;
this.keyManager = keyManager;
this.streamReaderFactory = streamReaderFactory;
this.streamWriterFactory = streamWriterFactory;
this.syncSessionFactory = syncSessionFactory;
this.handshakeManager = handshakeManager;
this.contactExchangeManager = contactExchangeManager;
this.connectionRegistry = connectionRegistry;
this.transportPropertyManager = transportPropertyManager;
this.secureRandom = secureRandom;
}
@Override
public void manageIncomingConnection(TransportId t,
TransportConnectionReader r) {
ioExecutor.execute(new IncomingSimplexSyncConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
syncSessionFactory, transportPropertyManager, t, r));
}
@Override
public void manageIncomingConnection(TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new IncomingDuplexSyncConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
syncSessionFactory, transportPropertyManager, ioExecutor,
t, d));
}
@Override
public void manageIncomingConnection(PendingContactId p, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new IncomingHandshakeConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
handshakeManager, contactExchangeManager, this, p, t, d));
}
@Override
public void manageOutgoingConnection(ContactId c, TransportId t,
TransportConnectionWriter w) {
ioExecutor.execute(new OutgoingSimplexSyncConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
syncSessionFactory, transportPropertyManager, c, t, w));
}
@Override
public void manageOutgoingConnection(ContactId c, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new OutgoingDuplexSyncConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
syncSessionFactory, transportPropertyManager, ioExecutor,
secureRandom, c, t, d));
}
@Override
public void manageOutgoingConnection(PendingContactId p, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new OutgoingHandshakeConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory,
handshakeManager, contactExchangeManager, this, p, t, d));
}
}

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class ConnectionModule {
@Provides
ConnectionManager provideConnectionManager(
ConnectionManagerImpl connectionManager) {
return connectionManager;
}
@Provides
@Singleton
ConnectionRegistry provideConnectionRegistry(
ConnectionRegistryImpl connectionRegistry) {
return connectionRegistry;
}
}

View File

@@ -1,270 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.connection.InterruptibleConnection;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.sync.Priority;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.Collections.emptyList;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
@ThreadSafe
@NotNullByDefault
class ConnectionRegistryImpl implements ConnectionRegistry {
private static final Logger LOG =
getLogger(ConnectionRegistryImpl.class.getName());
private final EventBus eventBus;
private final Map<TransportId, List<TransportId>> transportPrefs;
private final Object lock = new Object();
@GuardedBy("lock")
private final Map<ContactId, List<ConnectionRecord>> contactConnections;
@GuardedBy("lock")
private final Set<PendingContactId> connectedPendingContacts;
@Inject
ConnectionRegistryImpl(EventBus eventBus, PluginConfig pluginConfig) {
this.eventBus = eventBus;
transportPrefs = pluginConfig.getTransportPreferences();
contactConnections = new HashMap<>();
connectedPendingContacts = new HashSet<>();
}
@Override
public void registerConnection(ContactId c, TransportId t,
InterruptibleConnection conn, boolean incoming) {
if (LOG.isLoggable(INFO)) {
if (incoming) LOG.info("Incoming connection registered: " + t);
else LOG.info("Outgoing connection registered: " + t);
}
boolean firstConnection;
synchronized (lock) {
List<ConnectionRecord> recs = contactConnections.get(c);
if (recs == null) {
recs = new ArrayList<>();
contactConnections.put(c, recs);
}
firstConnection = recs.isEmpty();
recs.add(new ConnectionRecord(t, conn));
}
eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming));
if (firstConnection) {
LOG.info("Contact connected");
eventBus.broadcast(new ContactConnectedEvent(c));
}
}
@Override
public void setPriority(ContactId c, TransportId t,
InterruptibleConnection conn, Priority priority) {
if (LOG.isLoggable(INFO)) LOG.info("Setting connection priority: " + t);
List<InterruptibleConnection> toInterrupt;
boolean interruptNewConnection = false;
synchronized (lock) {
List<ConnectionRecord> recs = contactConnections.get(c);
if (recs == null) throw new IllegalArgumentException();
toInterrupt = new ArrayList<>(recs.size());
for (ConnectionRecord rec : recs) {
if (rec.conn == conn) {
// Store the priority of this connection
rec.priority = priority;
} else if (rec.priority != null) {
int compare = compareConnections(t, priority,
rec.transportId, rec.priority);
if (compare == -1) {
// The old connection is better than the new one
interruptNewConnection = true;
} else if (compare == 1 && !rec.interrupted) {
// The new connection is better than the old one
toInterrupt.add(rec.conn);
rec.interrupted = true;
}
}
}
}
if (interruptNewConnection) {
LOG.info("Interrupting new connection");
conn.interruptOutgoingSession();
}
for (InterruptibleConnection old : toInterrupt) {
LOG.info("Interrupting old connection");
old.interruptOutgoingSession();
}
}
private int compareConnections(TransportId tA, Priority pA, TransportId tB,
Priority pB) {
if (getBetterTransports(tA).contains(tB)) return -1;
if (getBetterTransports(tB).contains(tA)) return 1;
return tA.equals(tB) ? Bytes.compare(pA.getNonce(), pB.getNonce()) : 0;
}
private List<TransportId> getBetterTransports(TransportId t) {
List<TransportId> better = transportPrefs.get(t);
return better == null ? emptyList() : better;
}
@Override
public void unregisterConnection(ContactId c, TransportId t,
InterruptibleConnection conn, boolean incoming, boolean exception) {
if (LOG.isLoggable(INFO)) {
if (incoming) LOG.info("Incoming connection unregistered: " + t);
else LOG.info("Outgoing connection unregistered: " + t);
}
boolean lastConnection;
synchronized (lock) {
List<ConnectionRecord> recs = contactConnections.get(c);
if (recs == null || !recs.remove(new ConnectionRecord(t, conn)))
throw new IllegalArgumentException();
lastConnection = recs.isEmpty();
}
eventBus.broadcast(
new ConnectionClosedEvent(c, t, incoming, exception));
if (lastConnection) {
LOG.info("Contact disconnected");
eventBus.broadcast(new ContactDisconnectedEvent(c));
}
}
@Override
public Collection<ContactId> getConnectedContacts(TransportId t) {
synchronized (lock) {
List<ContactId> contactIds = new ArrayList<>();
for (Entry<ContactId, List<ConnectionRecord>> e :
contactConnections.entrySet()) {
for (ConnectionRecord rec : e.getValue()) {
if (rec.transportId.equals(t)) {
contactIds.add(e.getKey());
break;
}
}
}
if (LOG.isLoggable(INFO)) {
LOG.info(contactIds.size() + " contacts connected: " + t);
}
return contactIds;
}
}
@Override
public Collection<ContactId> getConnectedOrBetterContacts(TransportId t) {
synchronized (lock) {
List<TransportId> better = getBetterTransports(t);
List<ContactId> contactIds = new ArrayList<>();
for (Entry<ContactId, List<ConnectionRecord>> e :
contactConnections.entrySet()) {
for (ConnectionRecord rec : e.getValue()) {
if (rec.transportId.equals(t) ||
better.contains(rec.transportId)) {
contactIds.add(e.getKey());
break;
}
}
}
if (LOG.isLoggable(INFO)) {
LOG.info(contactIds.size()
+ " contacts connected or better: " + t);
}
return contactIds;
}
}
@Override
public boolean isConnected(ContactId c, TransportId t) {
synchronized (lock) {
List<ConnectionRecord> recs = contactConnections.get(c);
if (recs == null) return false;
for (ConnectionRecord rec : recs) {
if (rec.transportId.equals(t)) return true;
}
return false;
}
}
@Override
public boolean isConnected(ContactId c) {
synchronized (lock) {
return contactConnections.containsKey(c);
}
}
@Override
public boolean registerConnection(PendingContactId p) {
boolean added;
synchronized (lock) {
added = connectedPendingContacts.add(p);
}
if (added) eventBus.broadcast(new RendezvousConnectionOpenedEvent(p));
return added;
}
@Override
public void unregisterConnection(PendingContactId p, boolean success) {
synchronized (lock) {
if (!connectedPendingContacts.remove(p))
throw new IllegalArgumentException();
}
eventBus.broadcast(new RendezvousConnectionClosedEvent(p, success));
}
private static class ConnectionRecord {
private final TransportId transportId;
private final InterruptibleConnection conn;
@GuardedBy("lock")
@Nullable
private Priority priority = null;
@GuardedBy("lock")
private boolean interrupted = false;
private ConnectionRecord(TransportId transportId,
InterruptibleConnection conn) {
this.transportId = transportId;
this.conn = conn;
}
@Override
public boolean equals(Object o) {
if (o instanceof ConnectionRecord) {
return conn == ((ConnectionRecord) o).conn;
} else {
return false;
}
}
@Override
public int hashCode() {
return conn.hashCode();
}
}
}

View File

@@ -1,104 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.connection.InterruptibleConnection;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.util.concurrent.Executor;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@NotNullByDefault
abstract class DuplexSyncConnection extends SyncConnection
implements InterruptibleConnection {
final Executor ioExecutor;
final TransportId transportId;
final TransportConnectionReader reader;
final TransportConnectionWriter writer;
final TransportProperties remote;
private final Object interruptLock = new Object();
@GuardedBy("interruptLock")
@Nullable
private SyncSession outgoingSession = null;
@GuardedBy("interruptLock")
private boolean interruptWaiting = false;
@Override
public void interruptOutgoingSession() {
synchronized (interruptLock) {
if (outgoingSession == null) interruptWaiting = true;
else outgoingSession.interrupt();
}
}
void setOutgoingSession(SyncSession outgoingSession) {
synchronized (interruptLock) {
this.outgoingSession = outgoingSession;
if (interruptWaiting) {
outgoingSession.interrupt();
interruptWaiting = false;
}
}
}
DuplexSyncConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager,
Executor ioExecutor, TransportId transportId,
DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory,
transportPropertyManager);
this.ioExecutor = ioExecutor;
this.transportId = transportId;
reader = connection.getReader();
writer = connection.getWriter();
remote = connection.getRemoteProperties();
}
void onReadError(boolean recognised) {
disposeOnError(reader, recognised);
disposeOnError(writer);
interruptOutgoingSession();
}
void onWriteError() {
disposeOnError(reader, true);
disposeOnError(writer);
}
SyncSession createDuplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w, @Nullable Priority priority)
throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
ContactId c = requireNonNull(ctx.getContactId());
return syncSessionFactory.createDuplexOutgoingSession(c,
w.getMaxLatency(), w.getMaxIdleTime(), streamWriter, priority);
}
}

View File

@@ -1,72 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
abstract class HandshakeConnection extends Connection {
final HandshakeManager handshakeManager;
final ContactExchangeManager contactExchangeManager;
final ConnectionManager connectionManager;
final PendingContactId pendingContactId;
final TransportId transportId;
final DuplexTransportConnection connection;
final TransportConnectionReader reader;
final TransportConnectionWriter writer;
HandshakeConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
HandshakeManager handshakeManager,
ContactExchangeManager contactExchangeManager,
ConnectionManager connectionManager,
PendingContactId pendingContactId,
TransportId transportId, DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory);
this.handshakeManager = handshakeManager;
this.contactExchangeManager = contactExchangeManager;
this.connectionManager = connectionManager;
this.pendingContactId = pendingContactId;
this.transportId = transportId;
this.connection = connection;
reader = connection.getReader();
writer = connection.getWriter();
}
@Nullable
StreamContext allocateStreamContext(PendingContactId pendingContactId,
TransportId transportId) {
try {
return keyManager.getStreamContext(pendingContactId, transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
return null;
}
}
void onError(boolean recognised) {
disposeOnError(reader, recognised);
disposeOnError(writer);
}
}

View File

@@ -1,107 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.util.concurrent.Executor;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class IncomingDuplexSyncConnection extends DuplexSyncConnection
implements Runnable {
IncomingDuplexSyncConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager,
Executor ioExecutor, TransportId transportId,
DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory,
transportPropertyManager, ioExecutor, transportId, connection);
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctx = recogniseTag(reader, transportId);
if (ctx == null) {
LOG.info("Unrecognised tag");
onReadError(false);
return;
}
ContactId contactId = ctx.getContactId();
if (contactId == null) {
LOG.warning("Expected contact tag, got rendezvous tag");
onReadError(true);
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onReadError(true);
return;
}
connectionRegistry.registerConnection(contactId, transportId,
this, true);
// Start the outgoing session on another thread
ioExecutor.execute(() -> runOutgoingSession(contactId));
try {
// Store any transport properties discovered from the connection
transportPropertyManager.addRemotePropertiesFromConnection(
contactId, transportId, remote);
// Update the connection registry when we receive our priority
PriorityHandler handler = p -> connectionRegistry.setPriority(
contactId, transportId, this, p);
// Create and run the incoming session
createIncomingSession(ctx, reader, handler).run();
reader.dispose(false, true);
interruptOutgoingSession();
connectionRegistry.unregisterConnection(contactId, transportId,
this, true, false);
} catch (DbException | IOException e) {
logException(LOG, WARNING, e);
onReadError(true);
connectionRegistry.unregisterConnection(contactId, transportId,
this, true, true);
}
}
private void runOutgoingSession(ContactId contactId) {
// Allocate a stream context
StreamContext ctx = allocateStreamContext(contactId, transportId);
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onWriteError();
return;
}
try {
// Create and run the outgoing session
SyncSession out = createDuplexOutgoingSession(ctx, writer, null);
setOutgoingSession(out);
out.run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onWriteError();
}
}
}

View File

@@ -1,93 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.io.InputStream;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class IncomingHandshakeConnection extends HandshakeConnection
implements Runnable {
IncomingHandshakeConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
HandshakeManager handshakeManager,
ContactExchangeManager contactExchangeManager,
ConnectionManager connectionManager,
PendingContactId pendingContactId,
TransportId transportId, DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, handshakeManager, contactExchangeManager,
connectionManager, pendingContactId, transportId, connection);
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctxIn = recogniseTag(reader, transportId);
if (ctxIn == null) {
LOG.info("Unrecognised tag");
onError(false);
return;
}
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
if (inPendingContactId == null) {
LOG.warning("Expected rendezvous tag, got contact tag");
onError(true);
return;
}
// Allocate the outgoing stream context
StreamContext ctxOut =
allocateStreamContext(pendingContactId, transportId);
if (ctxOut == null) {
LOG.warning("Could not allocate stream context");
onError(true);
return;
}
// Close the connection if it's redundant
if (!connectionRegistry.registerConnection(pendingContactId)) {
LOG.info("Redundant rendezvous connection");
onError(true);
return;
}
// Handshake and exchange contacts
try {
InputStream in = streamReaderFactory.createStreamReader(
reader.getInputStream(), ctxIn);
// Flush the output stream to send the outgoing stream header
StreamWriter out = streamWriterFactory.createStreamWriter(
writer.getOutputStream(), ctxOut);
out.getOutputStream().flush();
HandshakeResult result =
handshakeManager.handshake(pendingContactId, in, out);
contactExchangeManager.exchangeContacts(pendingContactId,
connection, result.getMasterKey(), result.isAlice(), false);
connectionRegistry.unregisterConnection(pendingContactId, true);
// Reuse the connection as a transport connection
connectionManager.manageIncomingConnection(transportId, connection);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError(true);
connectionRegistry.unregisterConnection(pendingContactId, false);
}
}
}

View File

@@ -1,79 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
private final TransportId transportId;
private final TransportConnectionReader reader;
IncomingSimplexSyncConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager,
TransportId transportId, TransportConnectionReader reader) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory,
transportPropertyManager);
this.transportId = transportId;
this.reader = reader;
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctx = recogniseTag(reader, transportId);
if (ctx == null) {
LOG.info("Unrecognised tag");
onError(false);
return;
}
ContactId contactId = ctx.getContactId();
if (contactId == null) {
LOG.warning("Received rendezvous stream, expected contact");
onError(true);
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onError(true);
return;
}
try {
// We don't expect to receive a priority for this connection
PriorityHandler handler = p ->
LOG.info("Ignoring priority for simplex connection");
// Create and run the incoming session
createIncomingSession(ctx, reader, handler).run();
reader.dispose(false, true);
} catch (IOException e) {
logException(LOG, WARNING, e);
onError(true);
}
}
private void onError(boolean recognised) {
disposeOnError(reader, recognised);
}
}

View File

@@ -1,141 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class OutgoingDuplexSyncConnection extends DuplexSyncConnection
implements Runnable {
private final SecureRandom secureRandom;
private final ContactId contactId;
OutgoingDuplexSyncConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager,
Executor ioExecutor, SecureRandom secureRandom, ContactId contactId,
TransportId transportId, DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory,
transportPropertyManager, ioExecutor, transportId, connection);
this.secureRandom = secureRandom;
this.contactId = contactId;
}
@Override
public void run() {
// Allocate a stream context
StreamContext ctx = allocateStreamContext(contactId, transportId);
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onWriteError();
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Cannot use handshake mode stream context");
onWriteError();
return;
}
// Start the incoming session on another thread
Priority priority = generatePriority();
ioExecutor.execute(() -> runIncomingSession(priority));
try {
// Create and run the outgoing session
SyncSession out =
createDuplexOutgoingSession(ctx, writer, priority);
setOutgoingSession(out);
out.run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onWriteError();
}
}
private void runIncomingSession(Priority priority) {
// Read and recognise the tag
StreamContext ctx = recogniseTag(reader, transportId);
// Unrecognised tags are suspicious in this case
if (ctx == null) {
LOG.warning("Unrecognised tag for returning stream");
onReadError();
return;
}
// Check that the stream comes from the expected contact
ContactId inContactId = ctx.getContactId();
if (inContactId == null) {
LOG.warning("Expected contact tag, got rendezvous tag");
onReadError();
return;
}
if (!contactId.equals(inContactId)) {
LOG.warning("Wrong contact ID for returning stream");
onReadError();
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onReadError();
return;
}
connectionRegistry.registerConnection(contactId, transportId,
this, false);
connectionRegistry.setPriority(contactId, transportId, this, priority);
try {
// Store any transport properties discovered from the connection
transportPropertyManager.addRemotePropertiesFromConnection(
contactId, transportId, remote);
// We don't expect to receive a priority for this connection
PriorityHandler handler = p ->
LOG.info("Ignoring priority for outgoing connection");
// Create and run the incoming session
createIncomingSession(ctx, reader, handler).run();
reader.dispose(false, true);
interruptOutgoingSession();
connectionRegistry.unregisterConnection(contactId, transportId,
this, false, false);
} catch (DbException | IOException e) {
logException(LOG, WARNING, e);
onReadError();
connectionRegistry.unregisterConnection(contactId, transportId,
this, false, true);
}
}
private void onReadError() {
// 'Recognised' is always true for outgoing connections
onReadError(true);
}
private Priority generatePriority() {
byte[] nonce = new byte[PRIORITY_NONCE_BYTES];
secureRandom.nextBytes(nonce);
return new Priority(nonce);
}
}

View File

@@ -1,115 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.io.InputStream;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class OutgoingHandshakeConnection extends HandshakeConnection
implements Runnable {
OutgoingHandshakeConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
HandshakeManager handshakeManager,
ContactExchangeManager contactExchangeManager,
ConnectionManager connectionManager,
PendingContactId pendingContactId,
TransportId transportId, DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, handshakeManager, contactExchangeManager,
connectionManager, pendingContactId, transportId, connection);
}
@Override
public void run() {
// Allocate the outgoing stream context
StreamContext ctxOut =
allocateStreamContext(pendingContactId, transportId);
if (ctxOut == null) {
LOG.warning("Could not allocate stream context");
onError();
return;
}
// Flush the output stream to send the outgoing stream header
StreamWriter out;
try {
out = streamWriterFactory.createStreamWriter(
writer.getOutputStream(), ctxOut);
out.getOutputStream().flush();
} catch (IOException e) {
logException(LOG, WARNING, e);
onError();
return;
}
// Read and recognise the tag
StreamContext ctxIn = recogniseTag(reader, transportId);
// Unrecognised tags are suspicious in this case
if (ctxIn == null) {
LOG.warning("Unrecognised tag for returning stream");
onError();
return;
}
// Check that the stream comes from the expected pending contact
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
if (inPendingContactId == null) {
LOG.warning("Expected rendezvous tag, got contact tag");
onError();
return;
}
if (!inPendingContactId.equals(pendingContactId)) {
LOG.warning("Wrong pending contact ID for returning stream");
onError();
return;
}
// Close the connection if it's redundant
if (!connectionRegistry.registerConnection(pendingContactId)) {
LOG.info("Redundant rendezvous connection");
onError();
return;
}
// Handshake and exchange contacts
try {
InputStream in = streamReaderFactory.createStreamReader(
reader.getInputStream(), ctxIn);
HandshakeResult result =
handshakeManager.handshake(pendingContactId, in, out);
Contact contact = contactExchangeManager.exchangeContacts(
pendingContactId, connection, result.getMasterKey(),
result.isAlice(), false);
connectionRegistry.unregisterConnection(pendingContactId, true);
// Reuse the connection as a transport connection
connectionManager.manageOutgoingConnection(contact.getId(),
transportId, connection);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError();
connectionRegistry.unregisterConnection(pendingContactId, false);
}
}
private void onError() {
// 'Recognised' is always true for outgoing connections
onError(true);
}
}

View File

@@ -1,78 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class OutgoingSimplexSyncConnection extends SyncConnection implements Runnable {
private final ContactId contactId;
private final TransportId transportId;
private final TransportConnectionWriter writer;
OutgoingSimplexSyncConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager,
ContactId contactId, TransportId transportId,
TransportConnectionWriter writer) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory,
transportPropertyManager);
this.contactId = contactId;
this.transportId = transportId;
this.writer = writer;
}
@Override
public void run() {
// Allocate a stream context
StreamContext ctx = allocateStreamContext(contactId, transportId);
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onError();
return;
}
try {
// Create and run the outgoing session
createSimplexOutgoingSession(ctx, writer).run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onError();
}
}
private void onError() {
disposeOnError(writer);
}
private SyncSession createSimplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
ContactId c = requireNonNull(ctx.getContactId());
return syncSessionFactory.createSimplexOutgoingSession(c,
w.getMaxLatency(), streamWriter);
}
}

View File

@@ -1,64 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class SyncConnection extends Connection {
final SyncSessionFactory syncSessionFactory;
final TransportPropertyManager transportPropertyManager;
SyncConnection(KeyManager keyManager, ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager) {
super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory);
this.syncSessionFactory = syncSessionFactory;
this.transportPropertyManager = transportPropertyManager;
}
@Nullable
StreamContext allocateStreamContext(ContactId contactId,
TransportId transportId) {
try {
return keyManager.getStreamContext(contactId, transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
return null;
}
}
SyncSession createIncomingSession(StreamContext ctx,
TransportConnectionReader r, PriorityHandler handler)
throws IOException {
InputStream streamReader = streamReaderFactory.createStreamReader(
r.getInputStream(), ctx);
ContactId c = requireNonNull(ctx.getContactId());
return syncSessionFactory
.createIncomingSession(c, streamReader, handler);
}
}

View File

@@ -7,7 +7,6 @@ import net.i2p.crypto.eddsa.KeyPairGenerator;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey; import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey; import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser; import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.KeyStrengthener; import org.briarproject.bramble.api.crypto.KeyStrengthener;
@@ -40,9 +39,6 @@ import static java.lang.System.arraycopy;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT; import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE; import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_PASSWORD;
import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHENER_ERROR;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@@ -363,17 +359,16 @@ class CryptoComponentImpl implements CryptoComponent {
} }
@Override @Override
@Nullable
public byte[] decryptWithPassword(byte[] input, String password, public byte[] decryptWithPassword(byte[] input, String password,
@Nullable KeyStrengthener keyStrengthener) @Nullable KeyStrengthener keyStrengthener) {
throws DecryptionException {
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher(); AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
int macBytes = cipher.getMacBytes(); int macBytes = cipher.getMacBytes();
// The input contains the format version, salt, cost parameter, IV, // The input contains the format version, salt, cost parameter, IV,
// ciphertext and MAC // ciphertext and MAC
if (input.length < 1 + PBKDF_SALT_BYTES + INT_32_BYTES if (input.length < 1 + PBKDF_SALT_BYTES + INT_32_BYTES
+ STORAGE_IV_BYTES + macBytes) { + STORAGE_IV_BYTES + macBytes)
throw new DecryptionException(INVALID_CIPHERTEXT); return null; // Invalid input
}
int inputOff = 0; int inputOff = 0;
// Format version // Format version
byte formatVersion = input[inputOff]; byte formatVersion = input[inputOff];
@@ -381,7 +376,7 @@ class CryptoComponentImpl implements CryptoComponent {
// Check whether we support this format version // Check whether we support this format version
if (formatVersion != PBKDF_FORMAT_SCRYPT && if (formatVersion != PBKDF_FORMAT_SCRYPT &&
formatVersion != PBKDF_FORMAT_SCRYPT_STRENGTHENED) { formatVersion != PBKDF_FORMAT_SCRYPT_STRENGTHENED) {
throw new DecryptionException(INVALID_CIPHERTEXT); return null;
} }
// Salt // Salt
byte[] salt = new byte[PBKDF_SALT_BYTES]; byte[] salt = new byte[PBKDF_SALT_BYTES];
@@ -390,9 +385,8 @@ class CryptoComponentImpl implements CryptoComponent {
// Cost parameter // Cost parameter
long cost = ByteUtils.readUint32(input, inputOff); long cost = ByteUtils.readUint32(input, inputOff);
inputOff += INT_32_BYTES; inputOff += INT_32_BYTES;
if (cost < 2 || cost > Integer.MAX_VALUE) { if (cost < 2 || cost > Integer.MAX_VALUE)
throw new DecryptionException(INVALID_CIPHERTEXT); return null; // Invalid cost parameter
}
// IV // IV
byte[] iv = new byte[STORAGE_IV_BYTES]; byte[] iv = new byte[STORAGE_IV_BYTES];
arraycopy(input, inputOff, iv, 0, iv.length); arraycopy(input, inputOff, iv, 0, iv.length);
@@ -400,10 +394,8 @@ class CryptoComponentImpl implements CryptoComponent {
// Derive the decryption key from the password // Derive the decryption key from the password
SecretKey key = passwordBasedKdf.deriveKey(password, salt, (int) cost); SecretKey key = passwordBasedKdf.deriveKey(password, salt, (int) cost);
if (formatVersion == PBKDF_FORMAT_SCRYPT_STRENGTHENED) { if (formatVersion == PBKDF_FORMAT_SCRYPT_STRENGTHENED) {
if (keyStrengthener == null || !keyStrengthener.isInitialised()) { if (keyStrengthener == null || !keyStrengthener.isInitialised())
// Can't derive the same strengthened key return null; // Can't derive the same strengthened key
throw new DecryptionException(KEY_STRENGTHENER_ERROR);
}
key = keyStrengthener.strengthenKey(key); key = keyStrengthener.strengthenKey(key);
} }
// Initialise the cipher // Initialise the cipher
@@ -419,7 +411,7 @@ class CryptoComponentImpl implements CryptoComponent {
cipher.process(input, inputOff, inputLen, output, 0); cipher.process(input, inputOff, inputLen, output, 0);
return output; return output;
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new DecryptionException(INVALID_PASSWORD); return null; // Invalid ciphertext
} }
} }

View File

@@ -25,7 +25,6 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose; import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
import static org.briarproject.bramble.util.IoUtils.isNonEmptyDirectory;
import static org.briarproject.bramble.util.LogUtils.logFileOrDir; import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
/** /**
@@ -70,9 +69,8 @@ class H2Database extends JdbcDatabase {
LOG.info("Contents of account directory before opening DB:"); LOG.info("Contents of account directory before opening DB:");
logFileOrDir(LOG, INFO, dir.getParentFile()); logFileOrDir(LOG, INFO, dir.getParentFile());
} }
boolean reopen = isNonEmptyDirectory(dir); boolean reopen = !dir.mkdirs();
if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen); if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen);
if (!reopen && dir.mkdirs()) LOG.info("Created database directory");
super.open("org.h2.Driver", reopen, key, listener); super.open("org.h2.Driver", reopen, key, listener);
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Contents of account directory after opening DB:"); LOG.info("Contents of account directory after opening DB:");

View File

@@ -20,11 +20,9 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose; import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
import static org.briarproject.bramble.util.IoUtils.isNonEmptyDirectory;
/** /**
* Contains all the HSQLDB-specific code for the database. * Contains all the HSQLDB-specific code for the database.
@@ -66,10 +64,7 @@ class HyperSqlDatabase extends JdbcDatabase {
public boolean open(SecretKey key, @Nullable MigrationListener listener) public boolean open(SecretKey key, @Nullable MigrationListener listener)
throws DbException { throws DbException {
this.key = key; this.key = key;
File dir = config.getDatabaseDirectory(); boolean reopen = !config.getDatabaseDirectory().mkdirs();
boolean reopen = isNonEmptyDirectory(dir);
if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen);
if (!reopen && dir.mkdirs()) LOG.info("Created database directory");
super.open("org.hsqldb.jdbc.JDBCDriver", reopen, key, listener); super.open("org.hsqldb.jdbc.JDBCDriver", reopen, key, listener);
return reopen; return reopen;
} }

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class IoModule {
@Provides
@Singleton
TimeoutMonitor provideTimeoutMonitor(TimeoutMonitorImpl timeoutMonitor) {
return timeoutMonitor;
}
}

View File

@@ -1,104 +0,0 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.concurrent.GuardedBy;
@NotNullByDefault
class TimeoutInputStream extends InputStream {
private final Clock clock;
private final InputStream in;
private final long timeoutMs;
private final CloseListener listener;
private final Object lock = new Object();
@GuardedBy("lock")
private long readStartedMs = -1;
TimeoutInputStream(Clock clock, InputStream in, long timeoutMs,
CloseListener listener) {
this.clock = clock;
this.in = in;
this.timeoutMs = timeoutMs;
this.listener = listener;
}
@Override
public int read() throws IOException {
synchronized (lock) {
readStartedMs = clock.currentTimeMillis();
}
int input = in.read();
synchronized (lock) {
readStartedMs = -1;
}
return input;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
synchronized (lock) {
readStartedMs = clock.currentTimeMillis();
}
int read = in.read(b, off, len);
synchronized (lock) {
readStartedMs = -1;
}
return read;
}
@Override
public void close() throws IOException {
try {
in.close();
} finally {
listener.onClose(this);
}
}
@Override
public int available() throws IOException {
return in.available();
}
@Override
public void mark(int readlimit) {
in.mark(readlimit);
}
@Override
public boolean markSupported() {
return in.markSupported();
}
@Override
public void reset() throws IOException {
in.reset();
}
@Override
public long skip(long n) throws IOException {
return in.skip(n);
}
boolean hasTimedOut() {
synchronized (lock) {
return readStartedMs != -1 &&
clock.currentTimeMillis() - readStartedMs > timeoutMs;
}
}
interface CloseListener {
void onClose(TimeoutInputStream closed);
}
}

View File

@@ -1,96 +0,0 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.inject.Inject;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
class TimeoutMonitorImpl implements TimeoutMonitor {
private static final Logger LOG =
getLogger(TimeoutMonitorImpl.class.getName());
private static final long CHECK_INTERVAL_MS = SECONDS.toMillis(10);
private final ScheduledExecutorService scheduler;
private final Executor ioExecutor;
private final Clock clock;
private final Object lock = new Object();
@GuardedBy("lock")
private final List<TimeoutInputStream> streams = new ArrayList<>();
@GuardedBy("lock")
private Future<?> task = null;
@Inject
TimeoutMonitorImpl(@Scheduler ScheduledExecutorService scheduler,
@IoExecutor Executor ioExecutor, Clock clock) {
this.scheduler = scheduler;
this.ioExecutor = ioExecutor;
this.clock = clock;
}
@Override
public InputStream createTimeoutInputStream(InputStream in,
long timeoutMs) {
TimeoutInputStream stream = new TimeoutInputStream(clock, in,
timeoutMs, this::removeStream);
synchronized (lock) {
if (streams.isEmpty()) {
task = scheduler.scheduleWithFixedDelay(this::checkTimeouts,
CHECK_INTERVAL_MS, CHECK_INTERVAL_MS, MILLISECONDS);
}
streams.add(stream);
}
return stream;
}
private void removeStream(TimeoutInputStream stream) {
Future<?> toCancel = null;
synchronized (lock) {
if (streams.remove(stream) && streams.isEmpty()) {
toCancel = task;
task = null;
}
}
if (toCancel != null) toCancel.cancel(false);
}
@Scheduler
private void checkTimeouts() {
ioExecutor.execute(() -> {
List<TimeoutInputStream> snapshot;
synchronized (lock) {
snapshot = new ArrayList<>(streams);
}
for (TimeoutInputStream stream : snapshot) {
if (stream.hasTimedOut()) {
LOG.info("Input stream has timed out");
try {
stream.close();
} catch (IOException e) {
logException(LOG, INFO, e);
}
}
}
});
}
}

View File

@@ -0,0 +1,694 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.IoUtils.read;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class ConnectionManagerImpl implements ConnectionManager {
private static final Logger LOG =
getLogger(ConnectionManagerImpl.class.getName());
private final Executor ioExecutor;
private final KeyManager keyManager;
private final StreamReaderFactory streamReaderFactory;
private final StreamWriterFactory streamWriterFactory;
private final SyncSessionFactory syncSessionFactory;
private final HandshakeManager handshakeManager;
private final ContactExchangeManager contactExchangeManager;
private final ConnectionRegistry connectionRegistry;
@Inject
ConnectionManagerImpl(@IoExecutor Executor ioExecutor,
KeyManager keyManager, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory,
HandshakeManager handshakeManager,
ContactExchangeManager contactExchangeManager,
ConnectionRegistry connectionRegistry) {
this.ioExecutor = ioExecutor;
this.keyManager = keyManager;
this.streamReaderFactory = streamReaderFactory;
this.streamWriterFactory = streamWriterFactory;
this.syncSessionFactory = syncSessionFactory;
this.handshakeManager = handshakeManager;
this.contactExchangeManager = contactExchangeManager;
this.connectionRegistry = connectionRegistry;
}
@Override
public void manageIncomingConnection(TransportId t,
TransportConnectionReader r) {
ioExecutor.execute(new ManageIncomingSimplexConnection(t, r));
}
@Override
public void manageIncomingConnection(TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new ManageIncomingDuplexConnection(t, d));
}
@Override
public void manageIncomingConnection(PendingContactId p, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new ManageIncomingHandshakeConnection(p, t, d));
}
@Override
public void manageOutgoingConnection(ContactId c, TransportId t,
TransportConnectionWriter w) {
ioExecutor.execute(new ManageOutgoingSimplexConnection(c, t, w));
}
@Override
public void manageOutgoingConnection(ContactId c, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new ManageOutgoingDuplexConnection(c, t, d));
}
@Override
public void manageOutgoingConnection(PendingContactId p, TransportId t,
DuplexTransportConnection d) {
ioExecutor.execute(new ManageOutgoingHandshakeConnection(p, t, d));
}
private byte[] readTag(InputStream in) throws IOException {
byte[] tag = new byte[TAG_LENGTH];
read(in, tag);
return tag;
}
private SyncSession createIncomingSession(StreamContext ctx,
TransportConnectionReader r) throws IOException {
InputStream streamReader = streamReaderFactory.createStreamReader(
r.getInputStream(), ctx);
ContactId c = requireNonNull(ctx.getContactId());
return syncSessionFactory.createIncomingSession(c, streamReader);
}
private SyncSession createSimplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
ContactId c = requireNonNull(ctx.getContactId());
return syncSessionFactory.createSimplexOutgoingSession(c,
w.getMaxLatency(), streamWriter);
}
private SyncSession createDuplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
ContactId c = requireNonNull(ctx.getContactId());
return syncSessionFactory.createDuplexOutgoingSession(c,
w.getMaxLatency(), w.getMaxIdleTime(), streamWriter);
}
private void disposeOnError(TransportConnectionReader reader,
boolean recognised) {
try {
reader.dispose(true, recognised);
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private void disposeOnError(TransportConnectionWriter writer) {
try {
writer.dispose(true);
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private class ManageIncomingSimplexConnection implements Runnable {
private final TransportId transportId;
private final TransportConnectionReader reader;
private ManageIncomingSimplexConnection(TransportId transportId,
TransportConnectionReader reader) {
this.transportId = transportId;
this.reader = reader;
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctx;
try {
byte[] tag = readTag(reader.getInputStream());
ctx = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError(false);
return;
}
if (ctx == null) {
LOG.info("Unrecognised tag");
onError(false);
return;
}
ContactId contactId = ctx.getContactId();
if (contactId == null) {
LOG.warning("Received rendezvous stream, expected contact");
onError(true);
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onError(true);
return;
}
connectionRegistry.registerConnection(contactId, transportId, true);
try {
// Create and run the incoming session
createIncomingSession(ctx, reader).run();
reader.dispose(false, true);
} catch (IOException e) {
logException(LOG, WARNING, e);
onError(true);
} finally {
connectionRegistry.unregisterConnection(contactId, transportId,
true);
}
}
private void onError(boolean recognised) {
disposeOnError(reader, recognised);
}
}
private class ManageOutgoingSimplexConnection implements Runnable {
private final ContactId contactId;
private final TransportId transportId;
private final TransportConnectionWriter writer;
private ManageOutgoingSimplexConnection(ContactId contactId,
TransportId transportId, TransportConnectionWriter writer) {
this.contactId = contactId;
this.transportId = transportId;
this.writer = writer;
}
@Override
public void run() {
// Allocate a stream context
StreamContext ctx;
try {
ctx = keyManager.getStreamContext(contactId, transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
onError();
return;
}
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onError();
return;
}
connectionRegistry.registerConnection(contactId, transportId,
false);
try {
// Create and run the outgoing session
createSimplexOutgoingSession(ctx, writer).run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onError();
} finally {
connectionRegistry.unregisterConnection(contactId, transportId,
false);
}
}
private void onError() {
disposeOnError(writer);
}
}
private class ManageIncomingDuplexConnection implements Runnable {
private final TransportId transportId;
private final TransportConnectionReader reader;
private final TransportConnectionWriter writer;
@Nullable
private volatile SyncSession outgoingSession = null;
private ManageIncomingDuplexConnection(TransportId transportId,
DuplexTransportConnection connection) {
this.transportId = transportId;
reader = connection.getReader();
writer = connection.getWriter();
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctx;
try {
byte[] tag = readTag(reader.getInputStream());
ctx = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onReadError(false);
return;
}
if (ctx == null) {
LOG.info("Unrecognised tag");
onReadError(false);
return;
}
ContactId contactId = ctx.getContactId();
if (contactId == null) {
LOG.warning("Expected contact tag, got rendezvous tag");
onReadError(true);
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onReadError(true);
return;
}
connectionRegistry.registerConnection(contactId, transportId, true);
// Start the outgoing session on another thread
ioExecutor.execute(() -> runOutgoingSession(contactId));
try {
// Create and run the incoming session
createIncomingSession(ctx, reader).run();
reader.dispose(false, true);
// Interrupt the outgoing session so it finishes cleanly
SyncSession out = outgoingSession;
if (out != null) out.interrupt();
} catch (IOException e) {
logException(LOG, WARNING, e);
onReadError(true);
} finally {
connectionRegistry.unregisterConnection(contactId, transportId,
true);
}
}
private void runOutgoingSession(ContactId contactId) {
// Allocate a stream context
StreamContext ctx;
try {
ctx = keyManager.getStreamContext(contactId, transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
onWriteError();
return;
}
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onWriteError();
return;
}
try {
// Create and run the outgoing session
SyncSession out = createDuplexOutgoingSession(ctx, writer);
outgoingSession = out;
out.run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onWriteError();
}
}
private void onReadError(boolean recognised) {
disposeOnError(reader, recognised);
disposeOnError(writer);
// Interrupt the outgoing session so it finishes
SyncSession out = outgoingSession;
if (out != null) out.interrupt();
}
private void onWriteError() {
disposeOnError(reader, true);
disposeOnError(writer);
}
}
private class ManageOutgoingDuplexConnection implements Runnable {
private final ContactId contactId;
private final TransportId transportId;
private final TransportConnectionReader reader;
private final TransportConnectionWriter writer;
@Nullable
private volatile SyncSession outgoingSession = null;
private ManageOutgoingDuplexConnection(ContactId contactId,
TransportId transportId, DuplexTransportConnection connection) {
this.contactId = contactId;
this.transportId = transportId;
reader = connection.getReader();
writer = connection.getWriter();
}
@Override
public void run() {
// Allocate a stream context
StreamContext ctx;
try {
ctx = keyManager.getStreamContext(contactId, transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
onWriteError();
return;
}
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onWriteError();
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Cannot use handshake mode stream context");
onWriteError();
return;
}
// Start the incoming session on another thread
ioExecutor.execute(this::runIncomingSession);
try {
// Create and run the outgoing session
SyncSession out = createDuplexOutgoingSession(ctx, writer);
outgoingSession = out;
out.run();
writer.dispose(false);
} catch (IOException e) {
logException(LOG, WARNING, e);
onWriteError();
}
}
private void runIncomingSession() {
// Read and recognise the tag
StreamContext ctx;
try {
byte[] tag = readTag(reader.getInputStream());
ctx = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onReadError();
return;
}
// Unrecognised tags are suspicious in this case
if (ctx == null) {
LOG.warning("Unrecognised tag for returning stream");
onReadError();
return;
}
// Check that the stream comes from the expected contact
ContactId inContactId = ctx.getContactId();
if (inContactId == null) {
LOG.warning("Expected contact tag, got rendezvous tag");
onReadError();
return;
}
if (!contactId.equals(inContactId)) {
LOG.warning("Wrong contact ID for returning stream");
onReadError();
return;
}
if (ctx.isHandshakeMode()) {
// TODO: Support handshake mode for contacts
LOG.warning("Received handshake tag, expected rotation mode");
onReadError();
return;
}
connectionRegistry.registerConnection(contactId, transportId,
false);
try {
// Create and run the incoming session
createIncomingSession(ctx, reader).run();
reader.dispose(false, true);
// Interrupt the outgoing session so it finishes cleanly
SyncSession out = outgoingSession;
if (out != null) out.interrupt();
} catch (IOException e) {
logException(LOG, WARNING, e);
onReadError();
} finally {
connectionRegistry.unregisterConnection(contactId, transportId,
false);
}
}
private void onReadError() {
// 'Recognised' is always true for outgoing connections
disposeOnError(reader, true);
disposeOnError(writer);
// Interrupt the outgoing session so it finishes
SyncSession out = outgoingSession;
if (out != null) out.interrupt();
}
private void onWriteError() {
disposeOnError(reader, true);
disposeOnError(writer);
}
}
private class ManageIncomingHandshakeConnection implements Runnable {
private final PendingContactId pendingContactId;
private final TransportId transportId;
private final DuplexTransportConnection connection;
private final TransportConnectionReader reader;
private final TransportConnectionWriter writer;
private ManageIncomingHandshakeConnection(
PendingContactId pendingContactId, TransportId transportId,
DuplexTransportConnection connection) {
this.pendingContactId = pendingContactId;
this.transportId = transportId;
this.connection = connection;
reader = connection.getReader();
writer = connection.getWriter();
}
@Override
public void run() {
// Read and recognise the tag
StreamContext ctxIn;
try {
byte[] tag = readTag(reader.getInputStream());
ctxIn = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError(false);
return;
}
if (ctxIn == null) {
LOG.info("Unrecognised tag");
onError(false);
return;
}
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
if (inPendingContactId == null) {
LOG.warning("Expected rendezvous tag, got contact tag");
onError(true);
return;
}
// Allocate the outgoing stream context
StreamContext ctxOut;
try {
ctxOut = keyManager.getStreamContext(pendingContactId,
transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
onError(true);
return;
}
if (ctxOut == null) {
LOG.warning("Could not allocate stream context");
onError(true);
return;
}
// Close the connection if it's redundant
if (!connectionRegistry.registerConnection(pendingContactId)) {
LOG.info("Redundant rendezvous connection");
onError(true);
return;
}
// Handshake and exchange contacts
try {
InputStream in = streamReaderFactory.createStreamReader(
reader.getInputStream(), ctxIn);
// Flush the output stream to send the outgoing stream header
StreamWriter out = streamWriterFactory.createStreamWriter(
writer.getOutputStream(), ctxOut);
out.getOutputStream().flush();
HandshakeResult result = handshakeManager.handshake(
pendingContactId, in, out);
Contact contact = contactExchangeManager.exchangeContacts(
pendingContactId, connection, result.getMasterKey(),
result.isAlice(), false);
connectionRegistry.unregisterConnection(pendingContactId, true);
// Reuse the connection as a transport connection
manageOutgoingConnection(contact.getId(), transportId,
connection);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError(true);
connectionRegistry.unregisterConnection(pendingContactId,
false);
}
}
private void onError(boolean recognised) {
disposeOnError(reader, recognised);
disposeOnError(writer);
}
}
private class ManageOutgoingHandshakeConnection implements Runnable {
private final PendingContactId pendingContactId;
private final TransportId transportId;
private final DuplexTransportConnection connection;
private final TransportConnectionReader reader;
private final TransportConnectionWriter writer;
private ManageOutgoingHandshakeConnection(
PendingContactId pendingContactId, TransportId transportId,
DuplexTransportConnection connection) {
this.pendingContactId = pendingContactId;
this.transportId = transportId;
this.connection = connection;
reader = connection.getReader();
writer = connection.getWriter();
}
@Override
public void run() {
// Allocate the outgoing stream context
StreamContext ctxOut;
try {
ctxOut = keyManager.getStreamContext(pendingContactId,
transportId);
} catch (DbException e) {
logException(LOG, WARNING, e);
onError();
return;
}
if (ctxOut == null) {
LOG.warning("Could not allocate stream context");
onError();
return;
}
// Flush the output stream to send the outgoing stream header
StreamWriter out;
try {
out = streamWriterFactory.createStreamWriter(
writer.getOutputStream(), ctxOut);
out.getOutputStream().flush();
} catch (IOException e) {
logException(LOG, WARNING, e);
onError();
return;
}
// Read and recognise the tag
StreamContext ctxIn;
try {
byte[] tag = readTag(reader.getInputStream());
ctxIn = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError();
return;
}
// Unrecognised tags are suspicious in this case
if (ctxIn == null) {
LOG.warning("Unrecognised tag for returning stream");
onError();
return;
}
// Check that the stream comes from the expected pending contact
PendingContactId inPendingContactId = ctxIn.getPendingContactId();
if (inPendingContactId == null) {
LOG.warning("Expected rendezvous tag, got contact tag");
onError();
return;
}
if (!inPendingContactId.equals(pendingContactId)) {
LOG.warning("Wrong pending contact ID for returning stream");
onError();
return;
}
// Close the connection if it's redundant
if (!connectionRegistry.registerConnection(pendingContactId)) {
LOG.info("Redundant rendezvous connection");
onError();
return;
}
// Handshake and exchange contacts
try {
InputStream in = streamReaderFactory.createStreamReader(
reader.getInputStream(), ctxIn);
HandshakeResult result = handshakeManager.handshake(
pendingContactId, in, out);
Contact contact = contactExchangeManager.exchangeContacts(
pendingContactId, connection, result.getMasterKey(),
result.isAlice(), false);
connectionRegistry.unregisterConnection(pendingContactId, true);
// Reuse the connection as a transport connection
manageOutgoingConnection(contact.getId(), transportId,
connection);
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onError();
connectionRegistry.unregisterConnection(pendingContactId,
false);
}
}
private void onError() {
// 'Recognised' is always true for outgoing connections
disposeOnError(reader, true);
disposeOnError(writer);
}
}
}

View File

@@ -0,0 +1,150 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.Multiset;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
@ThreadSafe
@NotNullByDefault
class ConnectionRegistryImpl implements ConnectionRegistry {
private static final Logger LOG =
getLogger(ConnectionRegistryImpl.class.getName());
private final EventBus eventBus;
private final Object lock = new Object();
@GuardedBy("lock")
private final Map<TransportId, Multiset<ContactId>> contactConnections;
@GuardedBy("lock")
private final Multiset<ContactId> contactCounts;
@GuardedBy("lock")
private final Set<PendingContactId> connectedPendingContacts;
@Inject
ConnectionRegistryImpl(EventBus eventBus) {
this.eventBus = eventBus;
contactConnections = new HashMap<>();
contactCounts = new Multiset<>();
connectedPendingContacts = new HashSet<>();
}
@Override
public void registerConnection(ContactId c, TransportId t,
boolean incoming) {
if (LOG.isLoggable(INFO)) {
if (incoming) LOG.info("Incoming connection registered: " + t);
else LOG.info("Outgoing connection registered: " + t);
}
boolean firstConnection = false;
synchronized (lock) {
Multiset<ContactId> m = contactConnections.get(t);
if (m == null) {
m = new Multiset<>();
contactConnections.put(t, m);
}
m.add(c);
if (contactCounts.add(c) == 1) firstConnection = true;
}
eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming));
if (firstConnection) {
LOG.info("Contact connected");
eventBus.broadcast(new ContactConnectedEvent(c));
}
}
@Override
public void unregisterConnection(ContactId c, TransportId t,
boolean incoming) {
if (LOG.isLoggable(INFO)) {
if (incoming) LOG.info("Incoming connection unregistered: " + t);
else LOG.info("Outgoing connection unregistered: " + t);
}
boolean lastConnection = false;
synchronized (lock) {
Multiset<ContactId> m = contactConnections.get(t);
if (m == null || !m.contains(c))
throw new IllegalArgumentException();
m.remove(c);
if (contactCounts.remove(c) == 0) lastConnection = true;
}
eventBus.broadcast(new ConnectionClosedEvent(c, t, incoming));
if (lastConnection) {
LOG.info("Contact disconnected");
eventBus.broadcast(new ContactDisconnectedEvent(c));
}
}
@Override
public Collection<ContactId> getConnectedContacts(TransportId t) {
synchronized (lock) {
Multiset<ContactId> m = contactConnections.get(t);
if (m == null) return Collections.emptyList();
List<ContactId> ids = new ArrayList<>(m.keySet());
if (LOG.isLoggable(INFO))
LOG.info(ids.size() + " contacts connected: " + t);
return ids;
}
}
@Override
public boolean isConnected(ContactId c, TransportId t) {
synchronized (lock) {
Multiset<ContactId> m = contactConnections.get(t);
return m != null && m.contains(c);
}
}
@Override
public boolean isConnected(ContactId c) {
synchronized (lock) {
return contactCounts.contains(c);
}
}
@Override
public boolean registerConnection(PendingContactId p) {
boolean added;
synchronized (lock) {
added = connectedPendingContacts.add(p);
}
if (added) eventBus.broadcast(new RendezvousConnectionOpenedEvent(p));
return added;
}
@Override
public void unregisterConnection(PendingContactId p, boolean success) {
synchronized (lock) {
if (!connectedPendingContacts.remove(p))
throw new IllegalArgumentException();
}
eventBus.broadcast(new RendezvousConnectionClosedEvent(p, success));
}
}

View File

@@ -1,12 +1,12 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;

View File

@@ -3,6 +3,8 @@ package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
@@ -27,6 +29,20 @@ public class PluginModule {
return new BackoffFactoryImpl(); return new BackoffFactoryImpl();
} }
@Provides
@Singleton
ConnectionManager provideConnectionManager(
ConnectionManagerImpl connectionManager) {
return connectionManager;
}
@Provides
@Singleton
ConnectionRegistry provideConnectionRegistry(
ConnectionRegistryImpl connectionRegistry) {
return connectionRegistry;
}
@Provides @Provides
@Singleton @Singleton
PluginManager providePluginManager(LifecycleManager lifecycleManager, PluginManager providePluginManager(LifecycleManager lifecycleManager,

View File

@@ -1,8 +1,6 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -11,6 +9,8 @@ import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionHandler; import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
@@ -98,8 +98,8 @@ class PollerImpl implements Poller, EventListener {
ConnectionClosedEvent c = (ConnectionClosedEvent) e; ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased // Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId()); reschedule(c.getTransportId());
// If an outgoing connection failed, try to reconnect if (!c.isIncoming()) {
if (!c.isIncoming() && c.isException()) { // Connect to the disconnected contact
connectToContact(c.getContactId(), c.getTransportId()); connectToContact(c.getContactId(), c.getTransportId());
} }
} else if (e instanceof ConnectionOpenedEvent) { } else if (e instanceof ConnectionOpenedEvent) {
@@ -215,7 +215,7 @@ class PollerImpl implements Poller, EventListener {
Map<ContactId, TransportProperties> remote = Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(t); transportPropertyManager.getRemoteProperties(t);
Collection<ContactId> connected = Collection<ContactId> connected =
connectionRegistry.getConnectedOrBetterContacts(t); connectionRegistry.getConnectedContacts(t);
Collection<Pair<TransportProperties, ConnectionHandler>> Collection<Pair<TransportProperties, ConnectionHandler>>
properties = new ArrayList<>(); properties = new ArrayList<>();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) { for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {

View File

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection; import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
@@ -61,13 +60,12 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
getLogger(BluetoothPlugin.class.getName()); getLogger(BluetoothPlugin.class.getName());
final BluetoothConnectionLimiter connectionLimiter; final BluetoothConnectionLimiter connectionLimiter;
final TimeoutMonitor timeoutMonitor;
private final Executor ioExecutor; private final Executor ioExecutor;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final Backoff backoff; private final Backoff backoff;
private final PluginCallback callback; private final PluginCallback callback;
private final int maxLatency, maxIdleTime; private final int maxLatency;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean running = false, contactConnections = false; private volatile boolean running = false, contactConnections = false;
@@ -107,17 +105,14 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection discoverAndConnect(String uuid); abstract DuplexTransportConnection discoverAndConnect(String uuid);
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter, BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor, Executor ioExecutor, Executor ioExecutor, SecureRandom secureRandom,
SecureRandom secureRandom, Backoff backoff, Backoff backoff, PluginCallback callback, int maxLatency) {
PluginCallback callback, int maxLatency, int maxIdleTime) {
this.connectionLimiter = connectionLimiter; this.connectionLimiter = connectionLimiter;
this.timeoutMonitor = timeoutMonitor;
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.backoff = backoff; this.backoff = backoff;
this.callback = callback; this.callback = callback;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime;
} }
void onAdapterEnabled() { void onAdapterEnabled() {
@@ -146,7 +141,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override @Override
public int getMaxIdleTime() { public int getMaxIdleTime() {
return maxIdleTime; // Bluetooth detects dead connections so we don't need keepalives
return Integer.MAX_VALUE;
} }
@Override @Override
@@ -231,11 +227,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (LOG.isLoggable(INFO)) LOG.info(e.toString()); if (LOG.isLoggable(INFO)) LOG.info(e.toString());
return; return;
} }
LOG.info("Connection received"); backoff.reset();
if (connectionLimiter.contactConnectionOpened(conn)) { if (connectionLimiter.contactConnectionOpened(conn))
backoff.reset();
callback.handleConnection(conn); callback.handleConnection(conn);
}
if (!running) return; if (!running) return;
} }
} }
@@ -279,10 +273,13 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
String uuid = p.get(PROP_UUID); String uuid = p.get(PROP_UUID);
if (isNullOrEmpty(uuid)) return; if (isNullOrEmpty(uuid)) return;
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
if (!isRunning() || !shouldAllowContactConnections()) return;
if (!connectionLimiter.canOpenContactConnection()) return;
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
h.handleConnection(d); if (connectionLimiter.contactConnectionOpened(d))
h.handleConnection(d);
} }
}); });
} }
@@ -328,6 +325,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (isNullOrEmpty(uuid)) return null; if (isNullOrEmpty(uuid)) return null;
DuplexTransportConnection conn = connect(address, uuid); DuplexTransportConnection conn = connect(address, uuid);
if (conn == null) return null; if (conn == null) return null;
// TODO: Why don't we reset the backoff here?
return connectionLimiter.contactConnectionOpened(conn) ? conn : null; return connectionLimiter.contactConnectionOpened(conn) ? conn : null;
} }

View File

@@ -17,20 +17,18 @@ import java.io.IOException;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Random;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.lang.Integer.parseInt;
import static java.util.Collections.addAll; import static java.util.Collections.addAll;
import static java.util.Collections.emptyList;
import static java.util.Collections.sort; import static java.util.Collections.sort;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
@@ -39,7 +37,6 @@ import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TR
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID; import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_PORT;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@@ -50,36 +47,15 @@ class LanTcpPlugin extends TcpPlugin {
private static final Logger LOG = getLogger(LanTcpPlugin.class.getName()); private static final Logger LOG = getLogger(LanTcpPlugin.class.getName());
private static final LanAddressComparator ADDRESS_COMPARATOR =
new LanAddressComparator();
private static final int MAX_ADDRESSES = 4; private static final int MAX_ADDRESSES = 4;
private static final String SEPARATOR = ","; private static final String SEPARATOR = ",";
/**
* The IP address of an Android device providing a wifi access point.
*/
protected static final InetAddress WIFI_AP_ADDRESS;
/**
* The IP address of an Android device providing a wifi direct
* legacy mode access point.
*/
protected static final InetAddress WIFI_DIRECT_AP_ADDRESS;
static {
try {
WIFI_AP_ADDRESS = InetAddress.getByAddress(
new byte[] {(byte) 192, (byte) 168, 43, 1});
WIFI_DIRECT_AP_ADDRESS = InetAddress.getByAddress(
new byte[] {(byte) 192, (byte) 168, 49, 1});
} catch (UnknownHostException e) {
// Should only be thrown if the address has an illegal length
throw new AssertionError(e);
}
}
LanTcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback, LanTcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback,
int maxLatency, int maxIdleTime, int connectionTimeout) { int maxLatency, int maxIdleTime) {
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime, super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
connectionTimeout);
} }
@Override @Override
@@ -87,82 +63,38 @@ class LanTcpPlugin extends TcpPlugin {
return ID; return ID;
} }
@Override
public void start() {
if (used.getAndSet(true)) throw new IllegalStateException();
initialisePortProperty();
running = true;
bind();
}
protected void initialisePortProperty() {
TransportProperties p = callback.getLocalProperties();
if (isNullOrEmpty(p.get(PROP_PORT))) {
int port = new Random().nextInt(32768) + 32768;
p.put(PROP_PORT, String.valueOf(port));
callback.mergeLocalProperties(p);
}
}
@Override @Override
protected List<InetSocketAddress> getLocalSocketAddresses() { protected List<InetSocketAddress> getLocalSocketAddresses() {
// Use the same address and port as last time if available
TransportProperties p = callback.getLocalProperties(); TransportProperties p = callback.getLocalProperties();
int preferredPort = parsePortProperty(p.get(PROP_PORT));
String oldIpPorts = p.get(PROP_IP_PORTS); String oldIpPorts = p.get(PROP_IP_PORTS);
List<InetSocketAddress> olds = parseSocketAddresses(oldIpPorts); List<InetSocketAddress> olds = parseSocketAddresses(oldIpPorts);
List<InetSocketAddress> locals = new ArrayList<>(); List<InetSocketAddress> locals = new ArrayList<>();
List<InetSocketAddress> fallbacks = new ArrayList<>(); for (InetAddress local : getLocalIpAddresses()) {
for (InetAddress local : getUsableLocalInetAddresses()) { if (isAcceptableAddress(local)) {
// If we've used this address before, try to use the same port // If this is the old address, try to use the same port
int port = preferredPort; for (InetSocketAddress old : olds) {
for (InetSocketAddress old : olds) { if (old.getAddress().equals(local))
if (old.getAddress().equals(local)) { locals.add(new InetSocketAddress(local, old.getPort()));
port = old.getPort();
break;
} }
locals.add(new InetSocketAddress(local, 0));
} }
locals.add(new InetSocketAddress(local, port));
// Fall back to any available port
fallbacks.add(new InetSocketAddress(local, 0));
} }
locals.addAll(fallbacks); sort(locals, ADDRESS_COMPARATOR);
return locals; return locals;
} }
private int parsePortProperty(@Nullable String portProperty) {
if (isNullOrEmpty(portProperty)) return 0;
try {
return parseInt(portProperty);
} catch (NumberFormatException e) {
return 0;
}
}
private List<InetSocketAddress> parseSocketAddresses(String ipPorts) { private List<InetSocketAddress> parseSocketAddresses(String ipPorts) {
if (isNullOrEmpty(ipPorts)) return emptyList();
String[] split = ipPorts.split(SEPARATOR);
List<InetSocketAddress> addresses = new ArrayList<>(); List<InetSocketAddress> addresses = new ArrayList<>();
if (isNullOrEmpty(ipPorts)) return addresses; for (String ipPort : split) {
for (String ipPort : ipPorts.split(SEPARATOR)) {
InetSocketAddress a = parseSocketAddress(ipPort); InetSocketAddress a = parseSocketAddress(ipPort);
if (a != null) addresses.add(a); if (a != null) addresses.add(a);
} }
return addresses; return addresses;
} }
protected List<InetAddress> getUsableLocalInetAddresses() {
List<InterfaceAddress> ifAddrs =
new ArrayList<>(getLocalInterfaceAddresses());
// Prefer longer network prefixes
sort(ifAddrs, (a, b) ->
b.getNetworkPrefixLength() - a.getNetworkPrefixLength());
List<InetAddress> addrs = new ArrayList<>();
for (InterfaceAddress ifAddr : ifAddrs) {
InetAddress addr = ifAddr.getAddress();
if (isAcceptableAddress(addr)) addrs.add(addr);
}
return addrs;
}
@Override @Override
protected void setLocalSocketAddress(InetSocketAddress a) { protected void setLocalSocketAddress(InetSocketAddress a) {
String ipPort = getIpPortString(a); String ipPort = getIpPortString(a);
@@ -200,20 +132,7 @@ class LanTcpPlugin extends TcpPlugin {
@Override @Override
protected List<InetSocketAddress> getRemoteSocketAddresses( protected List<InetSocketAddress> getRemoteSocketAddresses(
TransportProperties p) { TransportProperties p) {
String ipPorts = p.get(PROP_IP_PORTS); return parseSocketAddresses(p.get(PROP_IP_PORTS));
List<InetSocketAddress> remotes = parseSocketAddresses(ipPorts);
int port = parsePortProperty(p.get(PROP_PORT));
// If the contact has a preferred port, we can guess their IP:port when
// they're providing a wifi access point
if (port != 0) {
InetSocketAddress wifiAp =
new InetSocketAddress(WIFI_AP_ADDRESS, port);
if (!remotes.contains(wifiAp)) remotes.add(wifiAp);
InetSocketAddress wifiDirectAp =
new InetSocketAddress(WIFI_DIRECT_AP_ADDRESS, port);
if (!remotes.contains(wifiDirectAp)) remotes.add(wifiDirectAp);
}
return remotes;
} }
private boolean isAcceptableAddress(InetAddress a) { private boolean isAcceptableAddress(InetAddress a) {
@@ -226,33 +145,52 @@ class LanTcpPlugin extends TcpPlugin {
} }
@Override @Override
protected boolean isConnectable(InterfaceAddress local, protected boolean isConnectable(InetSocketAddress remote) {
InetSocketAddress remote) {
if (remote.getPort() == 0) return false; if (remote.getPort() == 0) return false;
if (!isAcceptableAddress(remote.getAddress())) return false; if (!isAcceptableAddress(remote.getAddress())) return false;
// Try to determine whether the address is on the same LAN as us // Try to determine whether the address is on the same LAN as us
byte[] localIp = local.getAddress().getAddress(); if (socket == null) return false;
byte[] localIp = socket.getInetAddress().getAddress();
byte[] remoteIp = remote.getAddress().getAddress(); byte[] remoteIp = remote.getAddress().getAddress();
int prefixLength = local.getNetworkPrefixLength(); return addressesAreOnSameLan(localIp, remoteIp);
return areAddressesInSameNetwork(localIp, remoteIp, prefixLength);
} }
// Package access for testing // Package access for testing
static boolean areAddressesInSameNetwork(byte[] localIp, byte[] remoteIp, boolean addressesAreOnSameLan(byte[] localIp, byte[] remoteIp) {
int prefixLength) { // 10.0.0.0/8
if (localIp.length != remoteIp.length) return false; if (isPrefix10(localIp)) return isPrefix10(remoteIp);
// Compare the first prefixLength bits of the addresses // 172.16.0.0/12
for (int i = 0; i < prefixLength; i++) { if (isPrefix172(localIp)) return isPrefix172(remoteIp);
int byteIndex = i >> 3; // 192.168.0.0/16
int bitIndex = i & 7; // 0 to 7 if (isPrefix192(localIp)) return isPrefix192(remoteIp);
int mask = 128 >> bitIndex; // Select the bit at bitIndex // Unrecognised prefix - may be compatible
if ((localIp[byteIndex] & mask) != (remoteIp[byteIndex] & mask)) {
return false; // Addresses differ at bit i
}
}
return true; return true;
} }
private static boolean isPrefix10(byte[] ipv4) {
return ipv4[0] == 10;
}
private static boolean isPrefix172(byte[] ipv4) {
return ipv4[0] == (byte) 172 && (ipv4[1] & 0xF0) == 16;
}
private static boolean isPrefix192(byte[] ipv4) {
return ipv4[0] == (byte) 192 && ipv4[1] == (byte) 168;
}
// Returns the prefix length for an RFC 1918 address, or 0 for any other
// address
private static int getRfc1918PrefixLength(InetAddress addr) {
if (!(addr instanceof Inet4Address)) return 0;
if (!addr.isSiteLocalAddress()) return 0;
byte[] ipv4 = addr.getAddress();
if (isPrefix10(ipv4)) return 8;
if (isPrefix172(ipv4)) return 12;
if (isPrefix192(ipv4)) return 16;
return 0;
}
@Override @Override
public boolean supportsKeyAgreement() { public boolean supportsKeyAgreement() {
return true; return true;
@@ -291,12 +229,6 @@ class LanTcpPlugin extends TcpPlugin {
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor) { byte[] commitment, BdfList descriptor) {
if (!isRunning()) return null; if (!isRunning()) return null;
ServerSocket ss = socket;
InterfaceAddress local = getLocalInterfaceAddress(ss.getInetAddress());
if (local == null) {
LOG.warning("No interface for key agreement server socket");
return null;
}
InetSocketAddress remote; InetSocketAddress remote;
try { try {
remote = parseSocketAddress(descriptor); remote = parseSocketAddress(descriptor);
@@ -304,11 +236,12 @@ class LanTcpPlugin extends TcpPlugin {
LOG.info("Invalid IP/port in key agreement descriptor"); LOG.info("Invalid IP/port in key agreement descriptor");
return null; return null;
} }
if (!isConnectable(local, remote)) { if (!isConnectable(remote)) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
SocketAddress local = socket.getLocalSocketAddress();
LOG.info(scrubSocketAddress(remote) + LOG.info(scrubSocketAddress(remote) +
" is not connectable from " + " is not connectable from " +
scrubSocketAddress(ss.getLocalSocketAddress())); scrubSocketAddress(local));
} }
return null; return null;
} }
@@ -316,8 +249,8 @@ class LanTcpPlugin extends TcpPlugin {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubSocketAddress(remote)); LOG.info("Connecting to " + scrubSocketAddress(remote));
Socket s = createSocket(); Socket s = createSocket();
s.bind(new InetSocketAddress(ss.getInetAddress(), 0)); s.bind(new InetSocketAddress(socket.getInetAddress(), 0));
s.connect(remote, connectionTimeout); s.connect(remote);
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connected to " + scrubSocketAddress(remote)); LOG.info("Connected to " + scrubSocketAddress(remote));
@@ -366,4 +299,19 @@ class LanTcpPlugin extends TcpPlugin {
IoUtils.tryToClose(ss, LOG, WARNING); IoUtils.tryToClose(ss, LOG, WARNING);
} }
} }
static class LanAddressComparator implements Comparator<InetSocketAddress> {
@Override
public int compare(InetSocketAddress a, InetSocketAddress b) {
// Prefer addresses with non-zero ports
int aPort = a.getPort(), bPort = b.getPort();
if (aPort > 0 && bPort == 0) return -1;
if (aPort == 0 && bPort > 0) return 1;
// Prefer addresses with longer RFC 1918 prefixes
int aPrefix = getRfc1918PrefixLength(a.getAddress());
int bPrefix = getRfc1918PrefixLength(b.getAddress());
return bPrefix - aPrefix;
}
}
} }

View File

@@ -18,11 +18,10 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
@NotNullByDefault @NotNullByDefault
public class LanTcpPluginFactory implements DuplexPluginFactory { public class LanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30_000; // 30 seconds private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MAX_IDLE_TIME = 30_000; // 30 seconds private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
private static final int CONNECTION_TIMEOUT = 3_000; // 3 seconds private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
private static final int MIN_POLLING_INTERVAL = 60_000; // 1 minute private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor; private final Executor ioExecutor;
@@ -49,6 +48,6 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
return new LanTcpPlugin(ioExecutor, backoff, callback, MAX_LATENCY, return new LanTcpPlugin(ioExecutor, backoff, callback, MAX_LATENCY,
MAX_IDLE_TIME, CONNECTION_TIMEOUT); MAX_IDLE_TIME);
} }
} }

View File

@@ -19,10 +19,10 @@ import org.briarproject.bramble.util.IoUtils;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException; import java.net.SocketException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -36,6 +36,7 @@ import java.util.regex.Pattern;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static java.net.NetworkInterface.getNetworkInterfaces;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.list; import static java.util.Collections.list;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
@@ -57,8 +58,7 @@ abstract class TcpPlugin implements DuplexPlugin {
protected final Executor ioExecutor, bindExecutor; protected final Executor ioExecutor, bindExecutor;
protected final Backoff backoff; protected final Backoff backoff;
protected final PluginCallback callback; protected final PluginCallback callback;
protected final int maxLatency, maxIdleTime; protected final int maxLatency, maxIdleTime, socketTimeout;
protected final int connectionTimeout, socketTimeout;
protected final AtomicBoolean used = new AtomicBoolean(false); protected final AtomicBoolean used = new AtomicBoolean(false);
protected volatile boolean running = false; protected volatile boolean running = false;
@@ -86,18 +86,15 @@ abstract class TcpPlugin implements DuplexPlugin {
/** /**
* Returns true if connections to the given address can be attempted. * Returns true if connections to the given address can be attempted.
*/ */
@SuppressWarnings("BooleanMethodIsAlwaysInverted") protected abstract boolean isConnectable(InetSocketAddress remote);
protected abstract boolean isConnectable(InterfaceAddress local,
InetSocketAddress remote);
TcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback, TcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback,
int maxLatency, int maxIdleTime, int connectionTimeout) { int maxLatency, int maxIdleTime) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.backoff = backoff; this.backoff = backoff;
this.callback = callback; this.callback = callback;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime; this.maxIdleTime = maxIdleTime;
this.connectionTimeout = connectionTimeout;
if (maxIdleTime > Integer.MAX_VALUE / 2) if (maxIdleTime > Integer.MAX_VALUE / 2)
socketTimeout = Integer.MAX_VALUE; socketTimeout = Integer.MAX_VALUE;
else socketTimeout = maxIdleTime * 2; else socketTimeout = maxIdleTime * 2;
@@ -233,23 +230,13 @@ abstract class TcpPlugin implements DuplexPlugin {
@Override @Override
public DuplexTransportConnection createConnection(TransportProperties p) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (!isRunning()) return null; if (!isRunning()) return null;
ServerSocket ss = socket;
InterfaceAddress local = getLocalInterfaceAddress(ss.getInetAddress());
if (local == null) {
LOG.warning("No interface for server socket");
return null;
}
for (InetSocketAddress remote : getRemoteSocketAddresses(p)) { for (InetSocketAddress remote : getRemoteSocketAddresses(p)) {
// Don't try to connect to our own address if (!isConnectable(remote)) {
if (!canConnectToOwnAddress() &&
remote.getAddress().equals(ss.getInetAddress())) {
continue;
}
if (!isConnectable(local, remote)) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
SocketAddress local = socket.getLocalSocketAddress();
LOG.info(scrubSocketAddress(remote) + LOG.info(scrubSocketAddress(remote) +
" is not connectable from " + " is not connectable from " +
scrubSocketAddress(ss.getLocalSocketAddress())); scrubSocketAddress(local));
} }
continue; continue;
} }
@@ -257,8 +244,8 @@ abstract class TcpPlugin implements DuplexPlugin {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubSocketAddress(remote)); LOG.info("Connecting to " + scrubSocketAddress(remote));
Socket s = createSocket(); Socket s = createSocket();
s.bind(new InetSocketAddress(ss.getInetAddress(), 0)); s.bind(new InetSocketAddress(socket.getInetAddress(), 0));
s.connect(remote, connectionTimeout); s.connect(remote);
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connected to " + scrubSocketAddress(remote)); LOG.info("Connected to " + scrubSocketAddress(remote));
@@ -272,19 +259,6 @@ abstract class TcpPlugin implements DuplexPlugin {
return null; return null;
} }
@Nullable
InterfaceAddress getLocalInterfaceAddress(InetAddress a) {
for (InterfaceAddress ifAddr : getLocalInterfaceAddresses()) {
if (ifAddr.getAddress().equals(a)) return ifAddr;
}
return null;
}
// Override for testing
protected boolean canConnectToOwnAddress() {
return false;
}
protected Socket createSocket() throws IOException { protected Socket createSocket() throws IOException {
return new Socket(); return new Socket();
} }
@@ -340,27 +314,14 @@ abstract class TcpPlugin implements DuplexPlugin {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
List<InterfaceAddress> getLocalInterfaceAddresses() { Collection<InetAddress> getLocalIpAddresses() {
List<InterfaceAddress> addrs = new ArrayList<>();
for (NetworkInterface iface : getNetworkInterfaces()) {
addrs.addAll(iface.getInterfaceAddresses());
}
return addrs;
}
List<InetAddress> getLocalInetAddresses() {
List<InetAddress> addrs = new ArrayList<>();
for (NetworkInterface iface : getNetworkInterfaces()) {
addrs.addAll(list(iface.getInetAddresses()));
}
return addrs;
}
private List<NetworkInterface> getNetworkInterfaces() {
try { try {
Enumeration<NetworkInterface> ifaces = Enumeration<NetworkInterface> ifaces = getNetworkInterfaces();
NetworkInterface.getNetworkInterfaces(); if (ifaces == null) return emptyList();
return ifaces == null ? emptyList() : list(ifaces); List<InetAddress> addrs = new ArrayList<>();
for (NetworkInterface iface : list(ifaces))
addrs.addAll(list(iface.getInetAddresses()));
return addrs;
} catch (SocketException e) { } catch (SocketException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
return emptyList(); return emptyList();

View File

@@ -10,7 +10,6 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -30,10 +29,8 @@ class WanTcpPlugin extends TcpPlugin {
private volatile MappingResult mappingResult; private volatile MappingResult mappingResult;
WanTcpPlugin(Executor ioExecutor, Backoff backoff, PortMapper portMapper, WanTcpPlugin(Executor ioExecutor, Backoff backoff, PortMapper portMapper,
PluginCallback callback, int maxLatency, int maxIdleTime, PluginCallback callback, int maxLatency, int maxIdleTime) {
int connectionTimeout) { super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime,
connectionTimeout);
this.portMapper = portMapper; this.portMapper = portMapper;
} }
@@ -48,7 +45,7 @@ class WanTcpPlugin extends TcpPlugin {
TransportProperties p = callback.getLocalProperties(); TransportProperties p = callback.getLocalProperties();
InetSocketAddress old = parseSocketAddress(p.get(PROP_IP_PORT)); InetSocketAddress old = parseSocketAddress(p.get(PROP_IP_PORT));
List<InetSocketAddress> addrs = new LinkedList<>(); List<InetSocketAddress> addrs = new LinkedList<>();
for (InetAddress a : getLocalInetAddresses()) { for (InetAddress a : getLocalIpAddresses()) {
if (isAcceptableAddress(a)) { if (isAcceptableAddress(a)) {
// If this is the old address, try to use the same port // If this is the old address, try to use the same port
if (old != null && old.getAddress().equals(a)) if (old != null && old.getAddress().equals(a))
@@ -89,8 +86,7 @@ class WanTcpPlugin extends TcpPlugin {
} }
@Override @Override
protected boolean isConnectable(InterfaceAddress local, protected boolean isConnectable(InetSocketAddress remote) {
InetSocketAddress remote) {
if (remote.getPort() == 0) return false; if (remote.getPort() == 0) return false;
return isAcceptableAddress(remote.getAddress()); return isAcceptableAddress(remote.getAddress());
} }

View File

@@ -19,11 +19,10 @@ import static org.briarproject.bramble.api.plugin.WanTcpConstants.ID;
@NotNullByDefault @NotNullByDefault
public class WanTcpPluginFactory implements DuplexPluginFactory { public class WanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30_000; // 30 seconds private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MAX_IDLE_TIME = 30_000; // 30 seconds private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
private static final int CONNECTION_TIMEOUT = 30_000; // 30 seconds private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
private static final int MIN_POLLING_INTERVAL = 60_000; // 1 minute private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor; private final Executor ioExecutor;
@@ -53,6 +52,6 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
return new WanTcpPlugin(ioExecutor, backoff, return new WanTcpPlugin(ioExecutor, backoff,
new PortMapperImpl(shutdownManager), callback, MAX_LATENCY, new PortMapperImpl(shutdownManager), callback, MAX_LATENCY,
MAX_IDLE_TIME, CONNECTION_TIMEOUT); MAX_IDLE_TIME);
} }
} }

View File

@@ -37,11 +37,6 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.GROUP_KEY_DISCOVERED;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_TRANSPORT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_VERSION;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class TransportPropertyManagerImpl implements TransportPropertyManager, class TransportPropertyManagerImpl implements TransportPropertyManager,
@@ -116,10 +111,10 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
try { try {
// Find the latest update for this transport, if any // Find the latest update for this transport, if any
BdfDictionary d = metadataParser.parse(meta); BdfDictionary d = metadataParser.parse(meta);
TransportId t = new TransportId(d.getString(MSG_KEY_TRANSPORT_ID)); TransportId t = new TransportId(d.getString("transportId"));
LatestUpdate latest = findLatest(txn, m.getGroupId(), t, false); LatestUpdate latest = findLatest(txn, m.getGroupId(), t, false);
if (latest != null) { if (latest != null) {
if (d.getLong(MSG_KEY_VERSION) > latest.version) { if (d.getLong("version") > latest.version) {
// This update is newer - delete the previous update // This update is newer - delete the previous update
db.deleteMessage(txn, latest.messageId); db.deleteMessage(txn, latest.messageId);
db.deleteMessageMetadata(txn, latest.messageId); db.deleteMessageMetadata(txn, latest.messageId);
@@ -145,27 +140,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
} }
} }
@Override
public void addRemotePropertiesFromConnection(ContactId c, TransportId t,
TransportProperties props) throws DbException {
if (props.isEmpty()) return;
try {
db.transaction(false, txn -> {
Group g = getContactGroup(db.getContact(txn, c));
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(
txn, g.getId());
BdfDictionary discovered =
meta.getOptionalDictionary(GROUP_KEY_DISCOVERED);
if (discovered == null) discovered = new BdfDictionary();
discovered.putAll(props);
meta.put(GROUP_KEY_DISCOVERED, discovered);
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
});
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override @Override
public Map<TransportId, TransportProperties> getLocalProperties() public Map<TransportId, TransportProperties> getLocalProperties()
throws DbException { throws DbException {
@@ -229,26 +203,12 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
Group g = getContactGroup(c); Group g = getContactGroup(c);
try { try {
// Find the latest remote update // Find the latest remote update
TransportProperties remote;
LatestUpdate latest = findLatest(txn, g.getId(), t, false); LatestUpdate latest = findLatest(txn, g.getId(), t, false);
if (latest == null) { if (latest == null) return new TransportProperties();
remote = new TransportProperties(); // Retrieve and parse the latest remote properties
} else { BdfList message =
// Retrieve and parse the latest remote properties clientHelper.getMessageAsList(txn, latest.messageId);
BdfList message = return parseProperties(message);
clientHelper.getMessageAsList(txn, latest.messageId);
remote = parseProperties(message);
}
// Merge in any discovered properties
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
BdfDictionary d = meta.getOptionalDictionary(GROUP_KEY_DISCOVERED);
if (d == null) return remote;
TransportProperties merged =
clientHelper.parseAndValidateTransportProperties(d);
// Received properties override discovered properties
merged.putAll(remote);
return merged;
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }
@@ -321,9 +281,9 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
Message m = clientHelper.createMessage(g, now, body); Message m = clientHelper.createMessage(g, now, body);
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_TRANSPORT_ID, t.getString()); meta.put("transportId", t.getString());
meta.put(MSG_KEY_VERSION, version); meta.put("version", version);
meta.put(MSG_KEY_LOCAL, local); meta.put("local", local);
clientHelper.addLocalMessage(txn, m, meta, shared, false); clientHelper.addLocalMessage(txn, m, meta, shared, false);
} catch (FormatException e) { } catch (FormatException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -342,9 +302,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
.getMessageMetadataAsDictionary(txn, localGroup.getId()); .getMessageMetadataAsDictionary(txn, localGroup.getId());
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) { for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
BdfDictionary meta = e.getValue(); BdfDictionary meta = e.getValue();
TransportId t = TransportId t = new TransportId(meta.getString("transportId"));
new TransportId(meta.getString(MSG_KEY_TRANSPORT_ID)); long version = meta.getLong("version");
long version = meta.getLong(MSG_KEY_VERSION);
latestUpdates.put(t, new LatestUpdate(e.getKey(), version)); latestUpdates.put(t, new LatestUpdate(e.getKey(), version));
} }
return latestUpdates; return latestUpdates;
@@ -357,10 +316,9 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
clientHelper.getMessageMetadataAsDictionary(txn, g); clientHelper.getMessageMetadataAsDictionary(txn, g);
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) { for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
BdfDictionary meta = e.getValue(); BdfDictionary meta = e.getValue();
if (meta.getString(MSG_KEY_TRANSPORT_ID).equals(t.getString()) if (meta.getString("transportId").equals(t.getString())
&& meta.getBoolean(MSG_KEY_LOCAL) == local) { && meta.getBoolean("local") == local) {
return new LatestUpdate(e.getKey(), return new LatestUpdate(e.getKey(), meta.getLong("version"));
meta.getLong(MSG_KEY_VERSION));
} }
} }
return null; return null;

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.rendezvous;
import org.briarproject.bramble.PoliteExecutor; import org.briarproject.bramble.PoliteExecutor;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState; import org.briarproject.bramble.api.contact.PendingContactState;
@@ -24,6 +23,7 @@ import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionHandler; import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;

View File

@@ -14,7 +14,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.SyncRecordWriter; import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.SyncSession; import org.briarproject.bramble.api.sync.SyncSession;
@@ -36,7 +35,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -76,8 +74,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
private final int maxLatency, maxIdleTime; private final int maxLatency, maxIdleTime;
private final StreamWriter streamWriter; private final StreamWriter streamWriter;
private final SyncRecordWriter recordWriter; private final SyncRecordWriter recordWriter;
@Nullable
private final Priority priority;
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks; private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
private final AtomicBoolean generateAckQueued = new AtomicBoolean(false); private final AtomicBoolean generateAckQueued = new AtomicBoolean(false);
@@ -92,7 +88,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor, DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, Clock clock, ContactId contactId, int maxLatency, EventBus eventBus, Clock clock, ContactId contactId, int maxLatency,
int maxIdleTime, StreamWriter streamWriter, int maxIdleTime, StreamWriter streamWriter,
SyncRecordWriter recordWriter, @Nullable Priority priority) { SyncRecordWriter recordWriter) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
@@ -102,7 +98,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
this.maxIdleTime = maxIdleTime; this.maxIdleTime = maxIdleTime;
this.streamWriter = streamWriter; this.streamWriter = streamWriter;
this.recordWriter = recordWriter; this.recordWriter = recordWriter;
this.priority = priority;
writerTasks = new LinkedBlockingQueue<>(); writerTasks = new LinkedBlockingQueue<>();
} }
@@ -113,8 +108,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
try { try {
// Send our supported protocol versions // Send our supported protocol versions
recordWriter.writeVersions(new Versions(SUPPORTED_VERSIONS)); recordWriter.writeVersions(new Versions(SUPPORTED_VERSIONS));
// Send our connection priority, if this is an outgoing connection
if (priority != null) recordWriter.writePriority(priority);
// Start a query for each type of record // Start a query for each type of record
generateAck(); generateAck();
generateBatch(); generateBatch();
@@ -166,9 +159,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
dataToFlush = true; dataToFlush = true;
} }
} }
// Write any records that were already in the queue
ThrowingRunnable<IOException> task;
while ((task = writerTasks.poll()) != null) task.run();
streamWriter.sendEndOfStream(); streamWriter.sendEndOfStream();
} catch (InterruptedException e) { } catch (InterruptedException e) {
LOG.info("Interrupted while waiting for a record to write"); LOG.info("Interrupted while waiting for a record to write");

View File

@@ -15,8 +15,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.SyncRecordReader; import org.briarproject.bramble.api.sync.SyncRecordReader;
import org.briarproject.bramble.api.sync.SyncSession; import org.briarproject.bramble.api.sync.SyncSession;
@@ -49,19 +47,17 @@ class IncomingSession implements SyncSession, EventListener {
private final EventBus eventBus; private final EventBus eventBus;
private final ContactId contactId; private final ContactId contactId;
private final SyncRecordReader recordReader; private final SyncRecordReader recordReader;
private final PriorityHandler priorityHandler;
private volatile boolean interrupted = false; private volatile boolean interrupted = false;
IncomingSession(DatabaseComponent db, Executor dbExecutor, IncomingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, ContactId contactId, EventBus eventBus, ContactId contactId,
SyncRecordReader recordReader, PriorityHandler priorityHandler) { SyncRecordReader recordReader) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.contactId = contactId; this.contactId = contactId;
this.recordReader = recordReader; this.recordReader = recordReader;
this.priorityHandler = priorityHandler;
} }
@IoExecutor @IoExecutor
@@ -90,9 +86,6 @@ class IncomingSession implements SyncSession, EventListener {
} else if (recordReader.hasVersions()) { } else if (recordReader.hasVersions()) {
Versions v = recordReader.readVersions(); Versions v = recordReader.readVersions();
dbExecutor.execute(new ReceiveVersions(v)); dbExecutor.execute(new ReceiveVersions(v));
} else if (recordReader.hasPriority()) {
Priority p = recordReader.readPriority();
priorityHandler.handle(p);
} else { } else {
// unknown records are ignored in RecordReader#eof() // unknown records are ignored in RecordReader#eof()
throw new FormatException(); throw new FormatException();

View File

@@ -95,9 +95,6 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
if (task == CLOSE) break; if (task == CLOSE) break;
task.run(); task.run();
} }
// Write any records that were already in the queue
ThrowingRunnable<IOException> task;
while ((task = writerTasks.poll()) != null) task.run();
streamWriter.sendEndOfStream(); streamWriter.sendEndOfStream();
} catch (InterruptedException e) { } catch (InterruptedException e) {
LOG.info("Interrupted while waiting for a record to write"); LOG.info("Interrupted while waiting for a record to write");

View File

@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.SyncRecordReader; import org.briarproject.bramble.api.sync.SyncRecordReader;
import org.briarproject.bramble.api.sync.Versions; import org.briarproject.bramble.api.sync.Versions;
@@ -27,12 +26,10 @@ import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.bramble.api.sync.RecordTypes.ACK; import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE; import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER; import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST; import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS; import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
@NotThreadSafe @NotThreadSafe
@@ -51,7 +48,7 @@ class SyncRecordReaderImpl implements SyncRecordReader {
private static boolean isKnownRecordType(byte type) { private static boolean isKnownRecordType(byte type) {
return type == ACK || type == MESSAGE || type == OFFER || return type == ACK || type == MESSAGE || type == OFFER ||
type == REQUEST || type == VERSIONS || type == PRIORITY; type == REQUEST || type == VERSIONS;
} }
private final MessageFactory messageFactory; private final MessageFactory messageFactory;
@@ -177,23 +174,4 @@ class SyncRecordReaderImpl implements SyncRecordReader {
nextRecord = null; nextRecord = null;
return supported; return supported;
} }
@Override
public boolean hasPriority() throws IOException {
return !eof() && getNextRecordType() == PRIORITY;
}
@Override
public Priority readPriority() throws IOException {
if (!hasPriority()) throw new FormatException();
return new Priority(readNonce());
}
private byte[] readNonce() throws IOException {
if (nextRecord == null) throw new AssertionError();
byte[] payload = nextRecord.getPayload();
if (payload.length != PRIORITY_NONCE_BYTES) throw new FormatException();
nextRecord = null;
return payload;
}
} }

View File

@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.SyncRecordWriter; import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.Versions; import org.briarproject.bramble.api.sync.Versions;
@@ -21,7 +20,6 @@ import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.bramble.api.sync.RecordTypes.ACK; import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE; import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER; import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST; import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS; import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
@@ -75,12 +73,6 @@ class SyncRecordWriterImpl implements SyncRecordWriter {
writeRecord(VERSIONS); writeRecord(VERSIONS);
} }
@Override
public void writePriority(Priority p) throws IOException {
writer.writeRecord(
new Record(PROTOCOL_VERSION, PRIORITY, p.getNonce()));
}
@Override @Override
public void flush() throws IOException { public void flush() throws IOException {
writer.flush(); writer.flush();

View File

@@ -5,8 +5,6 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.PriorityHandler;
import org.briarproject.bramble.api.sync.SyncRecordReader; import org.briarproject.bramble.api.sync.SyncRecordReader;
import org.briarproject.bramble.api.sync.SyncRecordReaderFactory; import org.briarproject.bramble.api.sync.SyncRecordReaderFactory;
import org.briarproject.bramble.api.sync.SyncRecordWriter; import org.briarproject.bramble.api.sync.SyncRecordWriter;
@@ -20,7 +18,6 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
@@ -49,12 +46,10 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
} }
@Override @Override
public SyncSession createIncomingSession(ContactId c, InputStream in, public SyncSession createIncomingSession(ContactId c, InputStream in) {
PriorityHandler handler) {
SyncRecordReader recordReader = SyncRecordReader recordReader =
recordReaderFactory.createRecordReader(in); recordReaderFactory.createRecordReader(in);
return new IncomingSession(db, dbExecutor, eventBus, c, recordReader, return new IncomingSession(db, dbExecutor, eventBus, c, recordReader);
handler);
} }
@Override @Override
@@ -69,12 +64,11 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
@Override @Override
public SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency, public SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
int maxIdleTime, StreamWriter streamWriter, int maxIdleTime, StreamWriter streamWriter) {
@Nullable Priority priority) {
OutputStream out = streamWriter.getOutputStream(); OutputStream out = streamWriter.getOutputStream();
SyncRecordWriter recordWriter = SyncRecordWriter recordWriter =
recordWriterFactory.createRecordWriter(out); recordWriterFactory.createRecordWriter(out);
return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c, return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c,
maxLatency, maxIdleTime, streamWriter, recordWriter, priority); maxLatency, maxIdleTime, streamWriter, recordWriter);
} }
} }

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.account; package org.briarproject.bramble.account;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.KeyStrengthener; import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
@@ -20,15 +19,12 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.Charset;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_PASSWORD;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getIdentity; import static org.briarproject.bramble.test.TestUtils.getIdentity;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
@@ -39,7 +35,6 @@ import static org.briarproject.bramble.util.StringUtils.toHexString;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
public class AccountManagerImplTest extends BrambleMockTestCase { public class AccountManagerImplTest extends BrambleMockTestCase {
@@ -88,13 +83,8 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testSignInThrowsExceptionIfDbKeyCannotBeLoaded() { public void testSignInReturnsFalseIfDbKeyCannotBeLoaded() {
try { assertFalse(accountManager.signIn(password));
accountManager.signIn(password);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_CIPHERTEXT, expected.getDecryptionResult());
}
assertFalse(accountManager.hasDatabaseKey()); assertFalse(accountManager.hasDatabaseKey());
assertFalse(keyFile.exists()); assertFalse(keyFile.exists());
@@ -102,11 +92,11 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testSignInThrowsExceptionIfPasswordIsWrong() throws Exception { public void testSignInReturnsFalseIfPasswordIsWrong() throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).decryptWithPassword(encryptedKey, password, oneOf(crypto).decryptWithPassword(encryptedKey, password,
keyStrengthener); keyStrengthener);
will(throwException(new DecryptionException(INVALID_PASSWORD))); will(returnValue(null));
}}); }});
storeDatabaseKey(keyFile, encryptedKeyHex); storeDatabaseKey(keyFile, encryptedKeyHex);
@@ -115,12 +105,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
try { assertFalse(accountManager.signIn(password));
accountManager.signIn(password);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_PASSWORD, expected.getDecryptionResult());
}
assertFalse(accountManager.hasDatabaseKey()); assertFalse(accountManager.hasDatabaseKey());
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
@@ -143,7 +128,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
accountManager.signIn(password); assertTrue(accountManager.signIn(password));
assertTrue(accountManager.hasDatabaseKey()); assertTrue(accountManager.hasDatabaseKey());
SecretKey decrypted = accountManager.getDatabaseKey(); SecretKey decrypted = accountManager.getDatabaseKey();
assertNotNull(decrypted); assertNotNull(decrypted);
@@ -172,7 +157,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
accountManager.signIn(password); assertTrue(accountManager.signIn(password));
assertTrue(accountManager.hasDatabaseKey()); assertTrue(accountManager.hasDatabaseKey());
SecretKey decrypted = accountManager.getDatabaseKey(); SecretKey decrypted = accountManager.getDatabaseKey();
assertNotNull(decrypted); assertNotNull(decrypted);
@@ -254,6 +239,55 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
assertFalse(keyBackupFile.exists()); assertFalse(keyBackupFile.exists());
} }
@Test
public void testAccountExistsReturnsFalseIfDbDirectoryDoesNotExist()
throws Exception {
storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
assertFalse(dbDir.exists());
assertFalse(accountManager.accountExists());
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
assertFalse(dbDir.exists());
}
@Test
public void testAccountExistsReturnsFalseIfDbDirectoryIsNotDirectory()
throws Exception {
storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
assertTrue(dbDir.createNewFile());
assertFalse(dbDir.isDirectory());
assertFalse(accountManager.accountExists());
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
assertTrue(dbDir.exists());
assertFalse(dbDir.isDirectory());
}
@Test
public void testAccountExistsReturnsTrueIfDbDirectoryIsDirectory()
throws Exception {
storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
assertTrue(dbDir.mkdirs());
assertTrue(dbDir.isDirectory());
assertTrue(accountManager.accountExists());
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
assertTrue(dbDir.exists());
assertTrue(dbDir.isDirectory());
}
@Test @Test
public void testCreateAccountStoresDbKey() throws Exception { public void testCreateAccountStoresDbKey() throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -281,36 +315,26 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testChangePasswordThrowsExceptionIfDbKeyCannotBeLoaded() { public void testChangePasswordReturnsFalseIfDbKeyCannotBeLoaded() {
try { assertFalse(accountManager.changePassword(password, newPassword));
accountManager.changePassword(password, newPassword);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_CIPHERTEXT, expected.getDecryptionResult());
}
assertFalse(keyFile.exists()); assertFalse(keyFile.exists());
assertFalse(keyBackupFile.exists()); assertFalse(keyBackupFile.exists());
} }
@Test @Test
public void testChangePasswordThrowsExceptionIfPasswordIsWrong() public void testChangePasswordReturnsFalseIfPasswordIsWrong()
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).decryptWithPassword(encryptedKey, password, oneOf(crypto).decryptWithPassword(encryptedKey, password,
keyStrengthener); keyStrengthener);
will(throwException(new DecryptionException(INVALID_PASSWORD))); will(returnValue(null));
}}); }});
storeDatabaseKey(keyFile, encryptedKeyHex); storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex); storeDatabaseKey(keyBackupFile, encryptedKeyHex);
try { assertFalse(accountManager.changePassword(password, newPassword));
accountManager.changePassword(password, newPassword);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_PASSWORD, expected.getDecryptionResult());
}
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
@@ -333,7 +357,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
storeDatabaseKey(keyFile, encryptedKeyHex); storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex); storeDatabaseKey(keyBackupFile, encryptedKeyHex);
accountManager.changePassword(password, newPassword); assertTrue(accountManager.changePassword(password, newPassword));
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile)); assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile)); assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
@@ -342,7 +366,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
private void storeDatabaseKey(File f, String hex) throws IOException { private void storeDatabaseKey(File f, String hex) throws IOException {
f.getParentFile().mkdirs(); f.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(f); FileOutputStream out = new FileOutputStream(f);
out.write(hex.getBytes(Charset.forName("UTF-8"))); out.write(hex.getBytes("UTF-8"));
out.flush(); out.flush();
out.close(); out.close();
} }
@@ -350,7 +374,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
@Nullable @Nullable
private String loadDatabaseKey(File f) throws IOException { private String loadDatabaseKey(File f) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader( BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(f), Charset.forName("UTF-8"))); new FileInputStream(f), "UTF-8"));
String hex = reader.readLine(); String hex = reader.readLine();
reader.close(); reader.close();
return hex; return hex;

View File

@@ -1,600 +0,0 @@
package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.connection.InterruptibleConnection;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
import org.junit.Test;
import java.util.Collection;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.briarproject.bramble.util.StringUtils.fromHexString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class ConnectionRegistryImplTest extends BrambleMockTestCase {
private final EventBus eventBus = context.mock(EventBus.class);
private final PluginConfig pluginConfig = context.mock(PluginConfig.class);
private final InterruptibleConnection conn1 =
context.mock(InterruptibleConnection.class, "conn1");
private final InterruptibleConnection conn2 =
context.mock(InterruptibleConnection.class, "conn2");
private final InterruptibleConnection conn3 =
context.mock(InterruptibleConnection.class, "conn3");
private final ContactId contactId1 = getContactId();
private final ContactId contactId2 = getContactId();
private final TransportId transportId1 = getTransportId();
private final TransportId transportId2 = getTransportId();
private final TransportId transportId3 = getTransportId();
private final PendingContactId pendingContactId =
new PendingContactId(getRandomId());
private final Priority low =
new Priority(fromHexString("00000000000000000000000000000000"));
private final Priority high =
new Priority(fromHexString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"));
@Test
public void testRegisterMultipleConnections() {
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(emptyMap()));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// The registry should be empty
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId1));
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId2));
assertEquals(emptyList(), c.getConnectedContacts(transportId3));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId3));
// Check that a registered connection shows up - this should
// broadcast a ConnectionOpenedEvent and a ContactConnectedEvent
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerConnection(contactId1, transportId1, conn1, true);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Register another connection with the same contact and transport -
// this should broadcast a ConnectionOpenedEvent and lookup should be
// unaffected
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId1, transportId1, conn2, true);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Unregister one of the connections - this should broadcast a
// ConnectionClosedEvent and lookup should be unaffected
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
}});
c.unregisterConnection(contactId1, transportId1, conn1, true, false);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Unregister the other connection - this should broadcast a
// ConnectionClosedEvent and a ContactDisconnectedEvent
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
oneOf(eventBus).broadcast(with(any(
ContactDisconnectedEvent.class)));
}});
c.unregisterConnection(contactId1, transportId1, conn2, true, false);
context.assertIsSatisfied();
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId1));
// Try to unregister the connection again - exception should be thrown
try {
c.unregisterConnection(contactId1, transportId1, conn2,
true, false);
fail();
} catch (IllegalArgumentException expected) {
// Expected
}
}
@Test
public void testRegisterMultipleContacts() {
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(emptyMap()));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Register two contacts with one transport, then one of the contacts
// with a second transport - this should broadcast three
// ConnectionOpenedEvents and two ContactConnectedEvents
context.checking(new Expectations() {{
exactly(3).of(eventBus).broadcast(with(any(
ConnectionOpenedEvent.class)));
exactly(2).of(eventBus).broadcast(with(any(
ContactConnectedEvent.class)));
}});
c.registerConnection(contactId1, transportId1, conn1, true);
c.registerConnection(contactId2, transportId1, conn2, true);
c.registerConnection(contactId2, transportId2, conn3, true);
context.assertIsSatisfied();
Collection<ContactId> connected = c.getConnectedContacts(transportId1);
assertEquals(2, connected.size());
assertTrue(connected.contains(contactId1));
assertTrue(connected.contains(contactId2));
connected = c.getConnectedOrBetterContacts(transportId1);
assertEquals(2, connected.size());
assertTrue(connected.contains(contactId1));
assertTrue(connected.contains(contactId2));
assertEquals(singletonList(contactId2),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId2),
c.getConnectedOrBetterContacts(transportId2));
}
@Test
public void testConnectionsAreNotInterruptedUnlessPriorityIsSet() {
// Prefer transport 2 to transport 1
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(
singletonMap(transportId1, singletonList(transportId2))));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Connect via transport 1 (worse than 2) and set priority to low
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerConnection(contactId1, transportId1, conn1, true);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 2 (better than 1) and set priority to high -
// the old connection should not be interrupted, despite using a worse
// transport, to remain compatible with old peers
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId1, transportId2, conn2, true);
c.setPriority(contactId1, transportId2, conn2, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 3 (no preference) and set priority to high -
// again, no interruptions are expected
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId1, transportId3, conn3, true);
c.setPriority(contactId1, transportId3, conn3, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId3));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId3));
}
@Test
public void testNewConnectionIsInterruptedIfOldConnectionUsesBetterTransport() {
// Prefer transport 1 to transport 2
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(
singletonMap(transportId2, singletonList(transportId1))));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Connect via transport 1 (better than 2) and set priority to low
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerConnection(contactId1, transportId1, conn1, true);
c.setPriority(contactId1, transportId1, conn1, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// The contact is not connected via transport 2 but is connected via a
// better transport
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 2 (worse than 1) and set priority to high -
// the new connection should be interrupted because it uses a worse
// transport
context.checking(new Expectations() {{
oneOf(conn2).interruptOutgoingSession();
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId1, transportId2, conn2, true);
c.setPriority(contactId1, transportId2, conn2, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 3 (no preference) and set priority to low -
// no further interruptions
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId1, transportId3, conn3, true);
c.setPriority(contactId1, transportId3, conn3, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId3));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId3));
// Unregister the interrupted connection (transport 2)
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
}});
c.unregisterConnection(contactId1, transportId2, conn2, true, false);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// The contact is not connected via transport 2 but is connected via a
// better transport
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId3));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId3));
}
@Test
public void testOldConnectionIsInterruptedIfNewConnectionUsesBetterTransport() {
// Prefer transport 2 to transport 1
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(
singletonMap(transportId1, singletonList(transportId2))));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Connect via transport 1 (worse than 2) and set priority to high
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerConnection(contactId1, transportId1, conn1, true);
c.setPriority(contactId1, transportId1, conn1, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(emptyList(), c.getConnectedContacts(transportId2));
assertEquals(emptyList(), c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 2 (better than 1) and set priority to low -
// the old connection should be interrupted because it uses a worse
// transport
context.checking(new Expectations() {{
oneOf(conn1).interruptOutgoingSession();
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId1, transportId2, conn2, true);
c.setPriority(contactId1, transportId2, conn2, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
// Connect via transport 3 (no preference) and set priority to high -
// no further interruptions
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId1, transportId3, conn3, true);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId3));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId3));
// Unregister the interrupted connection (transport 1)
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
}});
c.unregisterConnection(contactId1, transportId1, conn1, true, false);
context.assertIsSatisfied();
// The contact is not connected via transport 1 but is connected via a
// better transport
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId2));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId3));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId3));
}
@Test
public void testNewConnectionIsInterruptedIfOldConnectionHasHigherPriority() {
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(emptyMap()));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Register a connection with high priority
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerConnection(contactId1, transportId1, conn1, true);
c.setPriority(contactId1, transportId1, conn1, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Register another connection via the same transport (no priority yet)
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId1, transportId1, conn2, true);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Set the priority of the second connection to low - the second
// connection should be interrupted
context.checking(new Expectations() {{
oneOf(conn2).interruptOutgoingSession();
}});
c.setPriority(contactId1, transportId1, conn2, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Register a third connection with low priority - it should also be
// interrupted
context.checking(new Expectations() {{
oneOf(conn3).interruptOutgoingSession();
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId1, transportId1, conn3, true);
c.setPriority(contactId1, transportId1, conn3, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
}
@Test
public void testOldConnectionIsInterruptedIfNewConnectionHasHigherPriority() {
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(emptyMap()));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
// Register a connection with low priority
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerConnection(contactId1, transportId1, conn1, true);
c.setPriority(contactId1, transportId1, conn1, low);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Register another connection via the same transport (no priority yet)
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId1, transportId1, conn2, true);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
// Set the priority of the second connection to high - the first
// connection should be interrupted
context.checking(new Expectations() {{
oneOf(conn1).interruptOutgoingSession();
}});
c.setPriority(contactId1, transportId1, conn2, high);
context.assertIsSatisfied();
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
assertEquals(singletonList(contactId1),
c.getConnectedOrBetterContacts(transportId1));
}
@Test
public void testRegisterAndUnregisterPendingContacts() {
context.checking(new Expectations() {{
allowing(pluginConfig).getTransportPreferences();
will(returnValue(emptyMap()));
}});
ConnectionRegistry c =
new ConnectionRegistryImpl(eventBus, pluginConfig);
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(
RendezvousConnectionOpenedEvent.class)));
}});
assertTrue(c.registerConnection(pendingContactId));
assertFalse(c.registerConnection(pendingContactId)); // Redundant
context.assertIsSatisfied();
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(
RendezvousConnectionClosedEvent.class)));
}});
c.unregisterConnection(pendingContactId, true);
context.assertIsSatisfied();
try {
c.unregisterConnection(pendingContactId, true);
fail();
} catch (IllegalArgumentException expected) {
// Expected
}
}
}

View File

@@ -2,13 +2,13 @@ package org.briarproject.bramble.contact;
import org.briarproject.bramble.BrambleCoreEagerSingletons; import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule; import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.ContactExchangeManager; import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule; import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;

View File

@@ -1,35 +1,25 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.system.SystemClock; import org.briarproject.bramble.system.SystemClock;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider; import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.jmock.Expectations; import org.briarproject.bramble.test.TestUtils;
import org.junit.Test; import org.junit.Test;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT; import java.util.Random;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_PASSWORD;
import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHENER_ERROR;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
public class PasswordBasedEncryptionTest extends BrambleMockTestCase { public class PasswordBasedEncryptionTest extends BrambleTestCase {
private final KeyStrengthener keyStrengthener =
context.mock(KeyStrengthener.class);
private final CryptoComponentImpl crypto = private final CryptoComponentImpl crypto =
new CryptoComponentImpl(new TestSecureRandomProvider(), new CryptoComponentImpl(new TestSecureRandomProvider(),
new ScryptKdf(new SystemClock())); new ScryptKdf(new SystemClock()));
@Test @Test
public void testEncryptionAndDecryption() throws Exception { public void testEncryptionAndDecryption() {
byte[] input = getRandomBytes(1234); byte[] input = TestUtils.getRandomBytes(1234);
String password = "password"; String password = "password";
byte[] ciphertext = crypto.encryptWithPassword(input, password, null); byte[] ciphertext = crypto.encryptWithPassword(input, password, null);
byte[] output = crypto.decryptWithPassword(ciphertext, password, null); byte[] output = crypto.decryptWithPassword(ciphertext, password, null);
@@ -37,80 +27,14 @@ public class PasswordBasedEncryptionTest extends BrambleMockTestCase {
} }
@Test @Test
public void testInvalidFormatVersionThrowsException() { public void testInvalidCiphertextReturnsNull() {
byte[] input = getRandomBytes(1234); byte[] input = TestUtils.getRandomBytes(1234);
String password = "password"; String password = "password";
byte[] ciphertext = crypto.encryptWithPassword(input, password, null); byte[] ciphertext = crypto.encryptWithPassword(input, password, null);
// Modify the ciphertext
// Modify the format version int position = new Random().nextInt(ciphertext.length);
ciphertext[0] ^= (byte) 0xFF; ciphertext[position] = (byte) (ciphertext[position] ^ 0xFF);
try { byte[] output = crypto.decryptWithPassword(ciphertext, password, null);
crypto.decryptWithPassword(ciphertext, password, null); assertNull(output);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_CIPHERTEXT, expected.getDecryptionResult());
}
}
@Test
public void testInvalidPasswordThrowsException() {
byte[] input = getRandomBytes(1234);
byte[] ciphertext = crypto.encryptWithPassword(input, "password", null);
// Try to decrypt with the wrong password
try {
crypto.decryptWithPassword(ciphertext, "wrong", null);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_PASSWORD, expected.getDecryptionResult());
}
}
@Test
public void testMissingKeyStrengthenerThrowsException() {
SecretKey strengthened = getSecretKey();
context.checking(new Expectations() {{
oneOf(keyStrengthener).strengthenKey(with(any(SecretKey.class)));
will(returnValue(strengthened));
}});
// Use the key strengthener during encryption
byte[] input = getRandomBytes(1234);
String password = "password";
byte[] ciphertext =
crypto.encryptWithPassword(input, password, keyStrengthener);
// The key strengthener is missing during decryption
try {
crypto.decryptWithPassword(ciphertext, password, null);
fail();
} catch (DecryptionException expected) {
assertEquals(KEY_STRENGTHENER_ERROR, expected.getDecryptionResult());
}
}
@Test
public void testKeyStrengthenerFailureThrowsException() {
SecretKey strengthened = getSecretKey();
context.checking(new Expectations() {{
oneOf(keyStrengthener).strengthenKey(with(any(SecretKey.class)));
will(returnValue(strengthened));
oneOf(keyStrengthener).isInitialised();
will(returnValue(false));
}});
// Use the key strengthener during encryption
byte[] input = getRandomBytes(1234);
String password = "password";
byte[] ciphertext =
crypto.encryptWithPassword(input, password, keyStrengthener);
// The key strengthener fails during decryption
try {
crypto.decryptWithPassword(ciphertext, password, keyStrengthener);
fail();
} catch (DecryptionException expected) {
assertEquals(KEY_STRENGTHENER_ERROR, expected.getDecryptionResult());
}
} }
} }

View File

@@ -1,143 +0,0 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.SettableClock;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class TimeoutInputStreamTest extends BrambleTestCase {
private static final long TIMEOUT_MS = MINUTES.toMillis(1);
private final long now = System.currentTimeMillis();
private AtomicLong time;
private UnresponsiveInputStream in;
private AtomicBoolean listenerCalled;
private TimeoutInputStream stream;
private CountDownLatch readReturned;
@Before
public void setUp() {
time = new AtomicLong(now);
in = new UnresponsiveInputStream();
listenerCalled = new AtomicBoolean(false);
stream = new TimeoutInputStream(new SettableClock(time), in,
TIMEOUT_MS, stream -> listenerCalled.set(true));
readReturned = new CountDownLatch(1);
}
@Test
public void testTimeoutIsReportedIfReadDoesNotReturn() throws Exception {
startReading();
try {
// The stream should not report a timeout
assertFalse(stream.hasTimedOut());
// Time passes
time.set(now + TIMEOUT_MS);
// The stream still shouldn't report a timeout
assertFalse(stream.hasTimedOut());
// Time passes
time.set(now + TIMEOUT_MS + 1);
// The stream should report a timeout
assertTrue(stream.hasTimedOut());
// The listener should not have been called yet
assertFalse(listenerCalled.get());
// Close the stream
stream.close();
// The listener should have been called
assertTrue(listenerCalled.get());
} finally {
// Allow the read to return
in.readFinished.countDown();
}
}
@Test
public void testTimeoutIsNotReportedIfReadReturns() throws Exception {
startReading();
try {
// The stream should not report a timeout
assertFalse(stream.hasTimedOut());
// Time passes
time.set(now + TIMEOUT_MS);
// The stream still shouldn't report a timeout
assertFalse(stream.hasTimedOut());
// Allow the read to finish and wait for it to return
in.readFinished.countDown();
readReturned.await(10, SECONDS);
// Time passes
time.set(now + TIMEOUT_MS + 1);
// The stream should not report a timeout as the read has returned
assertFalse(stream.hasTimedOut());
// The listener should not have been called yet
assertFalse(listenerCalled.get());
// Close the stream
stream.close();
// The listener should have been called
assertTrue(listenerCalled.get());
} finally {
// Allow the read to return in case an assertion was thrown
in.readFinished.countDown();
}
}
private void startReading() throws Exception {
// Start a background thread to read from the unresponsive stream
new Thread(() -> {
try {
assertEquals(123, stream.read());
readReturned.countDown();
} catch (IOException e) {
fail();
}
}).start();
// Wait for the background thread to start reading
assertTrue(in.readStarted.await(10, SECONDS));
}
private class UnresponsiveInputStream extends InputStream {
private final CountDownLatch readStarted = new CountDownLatch(1);
private final CountDownLatch readFinished = new CountDownLatch(1);
@Override
public int read() throws IOException {
readStarted.countDown();
try {
readFinished.await();
return 123;
} catch (InterruptedException e) {
throw new IOException(e);
}
}
}
}

View File

@@ -0,0 +1,149 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
import org.junit.Test;
import java.util.Collection;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class ConnectionRegistryImplTest extends BrambleMockTestCase {
private final EventBus eventBus = context.mock(EventBus.class);
private final ContactId contactId = getContactId();
private final ContactId contactId1 = getContactId();
private final TransportId transportId = getTransportId();
private final TransportId transportId1 = getTransportId();
private final PendingContactId pendingContactId =
new PendingContactId(getRandomId());
@Test
public void testRegisterAndUnregister() {
ConnectionRegistry c = new ConnectionRegistryImpl(eventBus);
// The registry should be empty
assertEquals(emptyList(), c.getConnectedContacts(transportId));
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
// Check that a registered connection shows up - this should
// broadcast a ConnectionOpenedEvent and a ContactConnectedEvent
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class)));
}});
c.registerConnection(contactId, transportId, true);
assertEquals(singletonList(contactId),
c.getConnectedContacts(transportId));
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
context.assertIsSatisfied();
// Register an identical connection - this should broadcast a
// ConnectionOpenedEvent and lookup should be unaffected
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
}});
c.registerConnection(contactId, transportId, true);
assertEquals(singletonList(contactId),
c.getConnectedContacts(transportId));
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
context.assertIsSatisfied();
// Unregister one of the connections - this should broadcast a
// ConnectionClosedEvent and lookup should be unaffected
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
}});
c.unregisterConnection(contactId, transportId, true);
assertEquals(singletonList(contactId),
c.getConnectedContacts(transportId));
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
context.assertIsSatisfied();
// Unregister the other connection - this should broadcast a
// ConnectionClosedEvent and a ContactDisconnectedEvent
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
oneOf(eventBus).broadcast(with(any(
ContactDisconnectedEvent.class)));
}});
c.unregisterConnection(contactId, transportId, true);
assertEquals(emptyList(), c.getConnectedContacts(transportId));
assertEquals(emptyList(), c.getConnectedContacts(transportId1));
context.assertIsSatisfied();
// Try to unregister the connection again - exception should be thrown
try {
c.unregisterConnection(contactId, transportId, true);
fail();
} catch (IllegalArgumentException expected) {
// Expected
}
// Register both contacts with one transport, one contact with both -
// this should broadcast three ConnectionOpenedEvents and two
// ContactConnectedEvents
context.checking(new Expectations() {{
exactly(3).of(eventBus).broadcast(with(any(
ConnectionOpenedEvent.class)));
exactly(2).of(eventBus).broadcast(with(any(
ContactConnectedEvent.class)));
}});
c.registerConnection(contactId, transportId, true);
c.registerConnection(contactId1, transportId, true);
c.registerConnection(contactId1, transportId1, true);
Collection<ContactId> connected = c.getConnectedContacts(transportId);
assertEquals(2, connected.size());
assertTrue(connected.contains(contactId));
assertTrue(connected.contains(contactId1));
assertEquals(singletonList(contactId1),
c.getConnectedContacts(transportId1));
}
@Test
public void testRegisterAndUnregisterPendingContacts() {
ConnectionRegistry c = new ConnectionRegistryImpl(eventBus);
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(
RendezvousConnectionOpenedEvent.class)));
}});
assertTrue(c.registerConnection(pendingContactId));
assertFalse(c.registerConnection(pendingContactId)); // Redundant
context.assertIsSatisfied();
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(
RendezvousConnectionClosedEvent.class)));
}});
c.unregisterConnection(pendingContactId, true);
context.assertIsSatisfied();
try {
c.unregisterConnection(pendingContactId, true);
fail();
} catch (IllegalArgumentException expected) {
// Expected
}
}
}

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginException; import org.briarproject.bramble.api.plugin.PluginException;

View File

@@ -1,10 +1,10 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.plugin.ConnectionHandler; import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
@@ -157,21 +157,7 @@ public class PollerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testRescheduleOnOutgoingConnectionClosed() { public void testRescheduleAndReconnectOnConnectionClosed()
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
}});
expectReschedule(plugin);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
false, false));
}
@Test
public void testRescheduleAndReconnectOnOutgoingConnectionFailed()
throws Exception { throws Exception {
DuplexPlugin plugin = context.mock(DuplexPlugin.class); DuplexPlugin plugin = context.mock(DuplexPlugin.class);
DuplexTransportConnection duplexConnection = DuplexTransportConnection duplexConnection =
@@ -180,40 +166,45 @@ public class PollerImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(plugin).getId(); allowing(plugin).getId();
will(returnValue(transportId)); will(returnValue(transportId));
// reschedule()
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule the next poll
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) pollingInterval), with(MILLISECONDS));
will(returnValue(future));
// connectToContact()
// Check whether the contact is already connected
oneOf(connectionRegistry).isConnected(contactId, transportId);
will(returnValue(false));
// Get the transport properties
oneOf(transportPropertyManager).getRemoteProperties(contactId,
transportId);
will(returnValue(properties));
// Connect to the contact
oneOf(plugin).createConnection(properties);
will(returnValue(duplexConnection));
// Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId,
transportId, duplexConnection);
}}); }});
expectReschedule(plugin);
expectReconnect(plugin, duplexConnection);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId, poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
false, true)); false));
}
@Test
public void testRescheduleOnIncomingConnectionClosed() {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
}});
expectReschedule(plugin);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
true, false));
}
@Test
public void testRescheduleOnIncomingConnectionFailed() {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
}});
expectReschedule(plugin);
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
true, false));
} }
@Test @Test
@@ -363,7 +354,7 @@ public class PollerImplTest extends BrambleMockTestCase {
// Get the transport properties and connected contacts // Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId); oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties))); will(returnValue(singletonMap(contactId, properties)));
oneOf(connectionRegistry).getConnectedOrBetterContacts(transportId); oneOf(connectionRegistry).getConnectedContacts(transportId);
will(returnValue(emptyList())); will(returnValue(emptyList()));
// Poll the plugin // Poll the plugin
oneOf(plugin).poll(with(collectionOf( oneOf(plugin).poll(with(collectionOf(
@@ -406,7 +397,7 @@ public class PollerImplTest extends BrambleMockTestCase {
// Get the transport properties and connected contacts // Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId); oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties))); will(returnValue(singletonMap(contactId, properties)));
oneOf(connectionRegistry).getConnectedOrBetterContacts(transportId); oneOf(connectionRegistry).getConnectedContacts(transportId);
will(returnValue(singletonList(contactId))); will(returnValue(singletonList(contactId)));
// All contacts are connected, so don't poll the plugin // All contacts are connected, so don't poll the plugin
}}); }});
@@ -440,48 +431,4 @@ public class PollerImplTest extends BrambleMockTestCase {
poller.eventOccurred(new TransportEnabledEvent(transportId)); poller.eventOccurred(new TransportEnabledEvent(transportId));
poller.eventOccurred(new TransportDisabledEvent(transportId)); poller.eventOccurred(new TransportDisabledEvent(transportId));
} }
private void expectReschedule(Plugin plugin) {
context.checking(new Expectations() {{
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule the next poll
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) pollingInterval), with(MILLISECONDS));
will(returnValue(future));
}});
}
private void expectReconnect(DuplexPlugin plugin,
DuplexTransportConnection duplexConnection) throws Exception {
context.checking(new Expectations() {{
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Check whether the contact is already connected
oneOf(connectionRegistry).isConnected(contactId, transportId);
will(returnValue(false));
// Get the transport properties
oneOf(transportPropertyManager).getRemoteProperties(contactId,
transportId);
will(returnValue(properties));
// Connect to the contact
oneOf(plugin).createConnection(properties);
will(returnValue(duplexConnection));
// Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId,
transportId, duplexConnection);
}});
}
} }

View File

@@ -7,11 +7,12 @@ import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.plugin.tcp.LanTcpPlugin.LanAddressComparator;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.io.IOException; import java.io.IOException;
@@ -21,6 +22,7 @@ import java.net.InetSocketAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.Comparator;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@@ -31,89 +33,56 @@ import static java.util.concurrent.Executors.newCachedThreadPool;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
import static org.briarproject.bramble.plugin.tcp.LanTcpPlugin.areAddressesInSameNetwork;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
public class LanTcpPluginTest extends BrambleTestCase { public class LanTcpPluginTest extends BrambleTestCase {
private final Backoff backoff = new TestBackoff(); private final Backoff backoff = new TestBackoff();
private final ExecutorService ioExecutor = newCachedThreadPool(); private final ExecutorService ioExecutor = newCachedThreadPool();
private Callback callback = null;
private LanTcpPlugin plugin = null;
@Before
public void setUp() {
callback = new Callback();
plugin = new LanTcpPlugin(ioExecutor, backoff, callback, 0, 0, 1000) {
@Override
protected boolean canConnectToOwnAddress() {
return true;
}
};
}
@Test @Test
public void testAreAddressesInSameNetwork() { public void testAddressesAreOnSameLan() {
// Local and remote in 10.0.0.0/8 Callback callback = new Callback();
assertTrue(areAddressesInSameNetwork(makeAddress(10, 0, 0, 0), LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback,
makeAddress(10, 255, 255, 255), 8)); 0, 0);
assertFalse(areAddressesInSameNetwork(makeAddress(10, 0, 0, 0), // Local and remote in 10.0.0.0/8 should return true
makeAddress(10, 255, 255, 255), 9)); assertTrue(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0),
makeAddress(10, 255, 255, 255)));
// Local and remote in 172.16.0.0/12 // Local and remote in 172.16.0.0/12 should return true
assertTrue(areAddressesInSameNetwork(makeAddress(172, 16, 0, 0), assertTrue(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0),
makeAddress(172, 31, 255, 255), 12)); makeAddress(172, 31, 255, 255)));
assertFalse(areAddressesInSameNetwork(makeAddress(172, 16, 0, 0), // Local and remote in 192.168.0.0/16 should return true
makeAddress(172, 31, 255, 255), 13)); assertTrue(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0),
makeAddress(192, 168, 255, 255)));
// Local and remote in 192.168.0.0/16 // Local and remote in 169.254.0.0/16 (link-local) should return true
assertTrue(areAddressesInSameNetwork(makeAddress(192, 168, 0, 0), assertTrue(plugin.addressesAreOnSameLan(makeAddress(169, 254, 0, 0),
makeAddress(192, 168, 255, 255), 16)); makeAddress(169, 254, 255, 255)));
assertFalse(areAddressesInSameNetwork(makeAddress(192, 168, 0, 0), // Local and remote in different recognised prefixes should return false
makeAddress(192, 168, 255, 255), 17)); assertFalse(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0),
makeAddress(172, 31, 255, 255)));
// Local and remote in 169.254.0.0/16 assertFalse(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0),
assertTrue(areAddressesInSameNetwork(makeAddress(169, 254, 0, 0), makeAddress(192, 168, 255, 255)));
makeAddress(169, 254, 255, 255), 16)); assertFalse(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0),
assertFalse(areAddressesInSameNetwork(makeAddress(169, 254, 0, 0), makeAddress(10, 255, 255, 255)));
makeAddress(169, 254, 255, 255), 17)); assertFalse(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0),
makeAddress(192, 168, 255, 255)));
// Local in 10.0.0.0/8, remote in a different network assertFalse(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0),
assertFalse(areAddressesInSameNetwork(makeAddress(10, 0, 0, 0), makeAddress(10, 255, 255, 255)));
makeAddress(172, 31, 255, 255), 8)); assertFalse(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0),
assertFalse(areAddressesInSameNetwork(makeAddress(10, 0, 0, 0), makeAddress(172, 31, 255, 255)));
makeAddress(192, 168, 255, 255), 8)); // Remote prefix unrecognised should return false
assertFalse(areAddressesInSameNetwork(makeAddress(10, 0, 0, 0), assertFalse(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0),
makeAddress(169, 254, 255, 255), 8)); makeAddress(1, 2, 3, 4)));
assertFalse(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0),
// Local in 172.16.0.0/12, remote in a different network makeAddress(1, 2, 3, 4)));
assertFalse(areAddressesInSameNetwork(makeAddress(172, 16, 0, 0), assertFalse(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0),
makeAddress(10, 255, 255, 255), 12)); makeAddress(1, 2, 3, 4)));
assertFalse(areAddressesInSameNetwork(makeAddress(172, 16, 0, 0), // Both prefixes unrecognised should return true (could be link-local)
makeAddress(192, 168, 255, 255), 12)); assertTrue(plugin.addressesAreOnSameLan(makeAddress(1, 2, 3, 4),
assertFalse(areAddressesInSameNetwork(makeAddress(172, 16, 0, 0), makeAddress(5, 6, 7, 8)));
makeAddress(169, 254, 255, 255), 12));
// Local in 192.168.0.0/16, remote in a different network
assertFalse(areAddressesInSameNetwork(makeAddress(192, 168, 0, 0),
makeAddress(10, 255, 255, 255), 16));
assertFalse(areAddressesInSameNetwork(makeAddress(192, 168, 0, 0),
makeAddress(172, 31, 255, 255), 16));
assertFalse(areAddressesInSameNetwork(makeAddress(192, 168, 0, 0),
makeAddress(169, 254, 255, 255), 16));
// Local in 169.254.0.0/16, remote in a different network
assertFalse(areAddressesInSameNetwork(makeAddress(169, 254, 0, 0),
makeAddress(10, 255, 255, 255), 16));
assertFalse(areAddressesInSameNetwork(makeAddress(169, 254, 0, 0),
makeAddress(172, 31, 255, 255), 16));
assertFalse(areAddressesInSameNetwork(makeAddress(169, 254, 0, 0),
makeAddress(192, 168, 255, 255), 16));
} }
private byte[] makeAddress(int... parts) { private byte[] makeAddress(int... parts) {
@@ -124,7 +93,13 @@ public class LanTcpPluginTest extends BrambleTestCase {
@Test @Test
public void testIncomingConnection() throws Exception { public void testIncomingConnection() throws Exception {
assumeTrue(systemHasLocalIpv4Address()); if (!systemHasLocalIpv4Address()) {
System.err.println("WARNING: Skipping test, no local IPv4 address");
return;
}
Callback callback = new Callback();
DuplexPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback,
0, 0);
plugin.start(); plugin.start();
// The plugin should have bound a socket and stored the port number // The plugin should have bound a socket and stored the port number
assertTrue(callback.propertiesLatch.await(5, SECONDS)); assertTrue(callback.propertiesLatch.await(5, SECONDS));
@@ -153,7 +128,13 @@ public class LanTcpPluginTest extends BrambleTestCase {
@Test @Test
public void testOutgoingConnection() throws Exception { public void testOutgoingConnection() throws Exception {
assumeTrue(systemHasLocalIpv4Address()); if (!systemHasLocalIpv4Address()) {
System.err.println("WARNING: Skipping test, no local IPv4 address");
return;
}
Callback callback = new Callback();
DuplexPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback,
0, 0);
plugin.start(); plugin.start();
// The plugin should have bound a socket and stored the port number // The plugin should have bound a socket and stored the port number
assertTrue(callback.propertiesLatch.await(5, SECONDS)); assertTrue(callback.propertiesLatch.await(5, SECONDS));
@@ -196,7 +177,13 @@ public class LanTcpPluginTest extends BrambleTestCase {
@Test @Test
public void testIncomingKeyAgreementConnection() throws Exception { public void testIncomingKeyAgreementConnection() throws Exception {
assumeTrue(systemHasLocalIpv4Address()); if (!systemHasLocalIpv4Address()) {
System.err.println("WARNING: Skipping test, no local IPv4 address");
return;
}
Callback callback = new Callback();
DuplexPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback,
0, 0);
plugin.start(); plugin.start();
assertTrue(callback.propertiesLatch.await(5, SECONDS)); assertTrue(callback.propertiesLatch.await(5, SECONDS));
KeyAgreementListener kal = KeyAgreementListener kal =
@@ -238,7 +225,13 @@ public class LanTcpPluginTest extends BrambleTestCase {
@Test @Test
public void testOutgoingKeyAgreementConnection() throws Exception { public void testOutgoingKeyAgreementConnection() throws Exception {
assumeTrue(systemHasLocalIpv4Address()); if (!systemHasLocalIpv4Address()) {
System.err.println("WARNING: Skipping test, no local IPv4 address");
return;
}
Callback callback = new Callback();
DuplexPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback,
0, 0);
plugin.start(); plugin.start();
// The plugin should have bound a socket and stored the port number // The plugin should have bound a socket and stored the port number
assertTrue(callback.propertiesLatch.await(5, SECONDS)); assertTrue(callback.propertiesLatch.await(5, SECONDS));
@@ -283,12 +276,62 @@ public class LanTcpPluginTest extends BrambleTestCase {
plugin.stop(); plugin.stop();
} }
@Test
public void testComparatorPrefersNonZeroPorts() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress nonZero = new InetSocketAddress("1.2.3.4", 1234);
InetSocketAddress zero = new InetSocketAddress("1.2.3.4", 0);
assertEquals(0, comparator.compare(nonZero, nonZero));
assertTrue(comparator.compare(nonZero, zero) < 0);
assertTrue(comparator.compare(zero, nonZero) > 0);
assertEquals(0, comparator.compare(zero, zero));
}
@Test
public void testComparatorPrefersLongerPrefixes() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0);
InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0);
InetSocketAddress prefix10 = new InetSocketAddress("10.0.0.1", 0);
assertEquals(0, comparator.compare(prefix192, prefix192));
assertTrue(comparator.compare(prefix192, prefix172) < 0);
assertTrue(comparator.compare(prefix192, prefix10) < 0);
assertTrue(comparator.compare(prefix172, prefix192) > 0);
assertEquals(0, comparator.compare(prefix172, prefix172));
assertTrue(comparator.compare(prefix172, prefix10) < 0);
assertTrue(comparator.compare(prefix10, prefix192) > 0);
assertTrue(comparator.compare(prefix10, prefix172) > 0);
assertEquals(0, comparator.compare(prefix10, prefix10));
}
@Test
public void testComparatorPrefersSiteLocalToLinkLocal() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0);
InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0);
InetSocketAddress prefix10 = new InetSocketAddress("10.0.0.1", 0);
InetSocketAddress linkLocal = new InetSocketAddress("169.254.0.1", 0);
assertTrue(comparator.compare(prefix192, linkLocal) < 0);
assertTrue(comparator.compare(prefix172, linkLocal) < 0);
assertTrue(comparator.compare(prefix10, linkLocal) < 0);
assertTrue(comparator.compare(linkLocal, prefix192) > 0);
assertTrue(comparator.compare(linkLocal, prefix172) > 0);
assertTrue(comparator.compare(linkLocal, prefix10) > 0);
assertEquals(0, comparator.compare(linkLocal, linkLocal));
}
private boolean systemHasLocalIpv4Address() throws Exception { private boolean systemHasLocalIpv4Address() throws Exception {
for (NetworkInterface i : list(getNetworkInterfaces())) { for (NetworkInterface i : list(getNetworkInterfaces())) {
for (InetAddress a : list(i.getInetAddresses())) { for (InetAddress a : list(i.getInetAddresses())) {
if (a instanceof Inet4Address) { if (a instanceof Inet4Address)
return a.isLinkLocalAddress() || a.isSiteLocalAddress(); return a.isLinkLocalAddress() || a.isSiteLocalAddress();
}
} }
} }
return false; return false;
@@ -297,9 +340,7 @@ public class LanTcpPluginTest extends BrambleTestCase {
@NotNullByDefault @NotNullByDefault
private static class Callback implements PluginCallback { private static class Callback implements PluginCallback {
// Properties will be stored twice: the preferred port at startup, private final CountDownLatch propertiesLatch = new CountDownLatch(1);
// and the IP:port when the server socket is bound
private final CountDownLatch propertiesLatch = new CountDownLatch(2);
private final CountDownLatch connectionsLatch = new CountDownLatch(1); private final CountDownLatch connectionsLatch = new CountDownLatch(1);
private final TransportProperties local = new TransportProperties(); private final TransportProperties local = new TransportProperties();

View File

@@ -24,18 +24,14 @@ import org.briarproject.bramble.test.DbExpectations;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.junit.Test; import org.junit.Test;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.GROUP_KEY_DISCOVERED;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_TRANSPORT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_VERSION;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID; import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION; import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
@@ -190,25 +186,25 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Message message = getMessage(contactGroupId); Message message = getMessage(contactGroupId);
Metadata meta = new Metadata(); Metadata meta = new Metadata();
BdfDictionary metaDictionary = BdfDictionary.of( BdfDictionary metaDictionary = BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 2), new BdfEntry("version", 2),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
); );
Map<MessageId, BdfDictionary> messageMetadata = Map<MessageId, BdfDictionary> messageMetadata =
new LinkedHashMap<>(); new LinkedHashMap<>();
// A remote update for another transport should be ignored // A remote update for another transport should be ignored
MessageId barUpdateId = new MessageId(getRandomId()); MessageId barUpdateId = new MessageId(getRandomId());
messageMetadata.put(barUpdateId, BdfDictionary.of( messageMetadata.put(barUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "bar"), new BdfEntry("transportId", "bar"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
)); ));
// A local update for the same transport should be ignored // A local update for the same transport should be ignored
MessageId localUpdateId = new MessageId(getRandomId()); MessageId localUpdateId = new MessageId(getRandomId());
messageMetadata.put(localUpdateId, BdfDictionary.of( messageMetadata.put(localUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -232,18 +228,18 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Metadata meta = new Metadata(); Metadata meta = new Metadata();
// Version 4 is being delivered // Version 4 is being delivered
BdfDictionary metaDictionary = BdfDictionary.of( BdfDictionary metaDictionary = BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 4), new BdfEntry("version", 4),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
); );
Map<MessageId, BdfDictionary> messageMetadata = Map<MessageId, BdfDictionary> messageMetadata =
new LinkedHashMap<>(); new LinkedHashMap<>();
// An older remote update for the same transport should be deleted // An older remote update for the same transport should be deleted
MessageId fooVersion3 = new MessageId(getRandomId()); MessageId fooVersion3 = new MessageId(getRandomId());
messageMetadata.put(fooVersion3, BdfDictionary.of( messageMetadata.put(fooVersion3, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 3), new BdfEntry("version", 3),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
)); ));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -269,18 +265,18 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Metadata meta = new Metadata(); Metadata meta = new Metadata();
// Version 3 is being delivered // Version 3 is being delivered
BdfDictionary metaDictionary = BdfDictionary.of( BdfDictionary metaDictionary = BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 3), new BdfEntry("version", 3),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
); );
Map<MessageId, BdfDictionary> messageMetadata = Map<MessageId, BdfDictionary> messageMetadata =
new LinkedHashMap<>(); new LinkedHashMap<>();
// A newer remote update for the same transport should not be deleted // A newer remote update for the same transport should not be deleted
MessageId fooVersion4 = new MessageId(getRandomId()); MessageId fooVersion4 = new MessageId(getRandomId());
messageMetadata.put(fooVersion4, BdfDictionary.of( messageMetadata.put(fooVersion4, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 4), new BdfEntry("version", 4),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
)); ));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -346,9 +342,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// A local update for another transport should be ignored // A local update for another transport should be ignored
MessageId barUpdateId = new MessageId(getRandomId()); MessageId barUpdateId = new MessageId(getRandomId());
messageMetadata.put(barUpdateId, BdfDictionary.of( messageMetadata.put(barUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "bar"), new BdfEntry("transportId", "bar"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
@@ -370,16 +366,16 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// A local update for another transport should be ignored // A local update for another transport should be ignored
MessageId barUpdateId = new MessageId(getRandomId()); MessageId barUpdateId = new MessageId(getRandomId());
messageMetadata.put(barUpdateId, BdfDictionary.of( messageMetadata.put(barUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "bar"), new BdfEntry("transportId", "bar"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
// A local update for the right transport should be returned // A local update for the right transport should be returned
MessageId fooUpdateId = new MessageId(getRandomId()); MessageId fooUpdateId = new MessageId(getRandomId());
messageMetadata.put(fooUpdateId, BdfDictionary.of( messageMetadata.put(fooUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict); BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict);
@@ -409,28 +405,28 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
List<Contact> contacts = asList(contact1, contact2); List<Contact> contacts = asList(contact1, contact2);
Group contactGroup1 = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup1 = getGroup(CLIENT_ID, MAJOR_VERSION);
Group contactGroup2 = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup2 = getGroup(CLIENT_ID, MAJOR_VERSION);
Map<MessageId, BdfDictionary> messageMetadata = Map<MessageId, BdfDictionary> messageMetadata2 =
new LinkedHashMap<>(); new LinkedHashMap<>();
// A remote update for another transport should be ignored // A remote update for another transport should be ignored
MessageId barUpdateId = new MessageId(getRandomId()); MessageId barUpdateId = new MessageId(getRandomId());
messageMetadata.put(barUpdateId, BdfDictionary.of( messageMetadata2.put(barUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "bar"), new BdfEntry("transportId", "bar"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
)); ));
// A local update for the right transport should be ignored // A local update for the right transport should be ignored
MessageId localUpdateId = new MessageId(getRandomId()); MessageId localUpdateId = new MessageId(getRandomId());
messageMetadata.put(localUpdateId, BdfDictionary.of( messageMetadata2.put(localUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
// A remote update for the right transport should be returned // A remote update for the right transport should be returned
MessageId fooUpdateId = new MessageId(getRandomId()); MessageId fooUpdateId = new MessageId(getRandomId());
messageMetadata.put(fooUpdateId, BdfDictionary.of( messageMetadata2.put(fooUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
)); ));
BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict); BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict);
@@ -444,25 +440,19 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
will(returnValue(contactGroup1)); will(returnValue(contactGroup1));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup1.getId()); contactGroup1.getId());
will(returnValue(emptyMap())); will(returnValue(Collections.emptyMap()));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup1.getId());
will(returnValue(new BdfDictionary()));
// Second contact: returns an update // Second contact: returns an update
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact2); MAJOR_VERSION, contact2);
will(returnValue(contactGroup2)); will(returnValue(contactGroup2));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup2.getId()); contactGroup2.getId());
will(returnValue(messageMetadata)); will(returnValue(messageMetadata2));
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId); oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
will(returnValue(fooUpdate)); will(returnValue(fooUpdate));
oneOf(clientHelper).parseAndValidateTransportProperties( oneOf(clientHelper).parseAndValidateTransportProperties(
fooPropertiesDict); fooPropertiesDict);
will(returnValue(fooProperties)); will(returnValue(fooProperties));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup2.getId());
will(returnValue(new BdfDictionary()));
}}); }});
TransportPropertyManagerImpl t = createInstance(); TransportPropertyManagerImpl t = createInstance();
@@ -473,62 +463,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
assertEquals(fooProperties, properties.get(contact2.getId())); assertEquals(fooProperties, properties.get(contact2.getId()));
} }
@Test
public void testReceivePropertiesOverrideDiscoveredProperties()
throws Exception {
Transaction txn = new Transaction(null, true);
Contact contact = getContact();
List<Contact> contacts = singletonList(contact);
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
MessageId updateId = new MessageId(getRandomId());
Map<MessageId, BdfDictionary> messageMetadata = singletonMap(updateId,
BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"),
new BdfEntry(MSG_KEY_VERSION, 1),
new BdfEntry(MSG_KEY_LOCAL, false)
));
BdfList update = BdfList.of("foo", 1, fooPropertiesDict);
TransportProperties discovered = new TransportProperties();
discovered.put("fooKey1", "overridden");
discovered.put("fooKey3", "fooValue3");
BdfDictionary discoveredDict = new BdfDictionary(discovered);
BdfDictionary groupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_DISCOVERED, discoveredDict)
);
TransportProperties merged = new TransportProperties();
merged.putAll(fooProperties);
merged.put("fooKey3", "fooValue3");
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getContacts(txn);
will(returnValue(contacts));
// One update
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(messageMetadata));
oneOf(clientHelper).getMessageAsList(txn, updateId);
will(returnValue(update));
oneOf(clientHelper).parseAndValidateTransportProperties(
fooPropertiesDict);
will(returnValue(fooProperties));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(groupMeta));
oneOf(clientHelper).parseAndValidateTransportProperties(
discoveredDict);
will(returnValue(discovered));
}});
TransportPropertyManagerImpl t = createInstance();
Map<ContactId, TransportProperties> properties =
t.getRemoteProperties(new TransportId("foo"));
assertEquals(merged, properties.get(contact.getId()));
}
@Test @Test
public void testMergingUnchangedPropertiesDoesNotCreateUpdate() public void testMergingUnchangedPropertiesDoesNotCreateUpdate()
throws Exception { throws Exception {
@@ -536,9 +470,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
MessageId updateId = new MessageId(getRandomId()); MessageId updateId = new MessageId(getRandomId());
Map<MessageId, BdfDictionary> messageMetadata = singletonMap(updateId, Map<MessageId, BdfDictionary> messageMetadata = singletonMap(updateId,
BdfDictionary.of( BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
BdfList update = BdfList.of("foo", 1, fooPropertiesDict); BdfList update = BdfList.of("foo", 1, fooPropertiesDict);
@@ -571,7 +505,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// There are no existing properties to merge with // There are no existing properties to merge with
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
localGroup.getId()); localGroup.getId());
will(returnValue(emptyMap())); will(returnValue(Collections.emptyMap()));
// Store the new properties in the local group, version 1 // Store the new properties in the local group, version 1
expectStoreMessage(txn, localGroup.getId(), "foo", expectStoreMessage(txn, localGroup.getId(), "foo",
fooPropertiesDict, 1, true, false); fooPropertiesDict, 1, true, false);
@@ -583,7 +517,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId()); contactGroup.getId());
will(returnValue(emptyMap())); will(returnValue(Collections.emptyMap()));
expectStoreMessage(txn, contactGroup.getId(), "foo", expectStoreMessage(txn, contactGroup.getId(), "foo",
fooPropertiesDict, 1, true, true); fooPropertiesDict, 1, true, true);
}}); }});
@@ -598,9 +532,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Contact contact = getContact(); Contact contact = getContact();
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
BdfDictionary oldMetadata = BdfDictionary.of( BdfDictionary oldMetadata = BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
); );
MessageId localGroupUpdateId = new MessageId(getRandomId()); MessageId localGroupUpdateId = new MessageId(getRandomId());
Map<MessageId, BdfDictionary> localGroupMessageMetadata = Map<MessageId, BdfDictionary> localGroupMessageMetadata =
@@ -655,14 +589,14 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// The latest update for transport "foo" should be returned // The latest update for transport "foo" should be returned
MessageId fooVersion999 = new MessageId(getRandomId()); MessageId fooVersion999 = new MessageId(getRandomId());
messageMetadata.put(fooVersion999, BdfDictionary.of( messageMetadata.put(fooVersion999, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 999) new BdfEntry("version", 999)
)); ));
// The latest update for transport "bar" should be returned // The latest update for transport "bar" should be returned
MessageId barVersion3 = new MessageId(getRandomId()); MessageId barVersion3 = new MessageId(getRandomId());
messageMetadata.put(barVersion3, BdfDictionary.of( messageMetadata.put(barVersion3, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "bar"), new BdfEntry("transportId", "bar"),
new BdfEntry(MSG_KEY_VERSION, 3) new BdfEntry("version", 3)
)); ));
BdfList fooUpdate = BdfList.of("foo", 999, fooPropertiesDict); BdfList fooUpdate = BdfList.of("foo", 999, fooPropertiesDict);
BdfList barUpdate = BdfList.of("bar", 3, barPropertiesDict); BdfList barUpdate = BdfList.of("bar", 3, barPropertiesDict);
@@ -693,9 +627,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Message message = getMessage(g); Message message = getMessage(g);
long timestamp = message.getTimestamp(); long timestamp = message.getTimestamp();
BdfDictionary meta = BdfDictionary.of( BdfDictionary meta = BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, transportId), new BdfEntry("transportId", transportId),
new BdfEntry(MSG_KEY_VERSION, version), new BdfEntry("version", version),
new BdfEntry(MSG_KEY_LOCAL, local) new BdfEntry("local", local)
); );
context.checking(new Expectations() {{ context.checking(new Expectations() {{

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.rendezvous; package org.briarproject.bramble.rendezvous;
import org.briarproject.bramble.api.connection.ConnectionManager;
import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactState; import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent; import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent;
@@ -14,6 +13,7 @@ import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.plugin.ConnectionHandler; import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;

View File

@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.record.RecordReader;
import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Priority;
import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.SyncRecordReader; import org.briarproject.bramble.api.sync.SyncRecordReader;
import org.briarproject.bramble.api.sync.Versions; import org.briarproject.bramble.api.sync.Versions;
@@ -25,14 +24,11 @@ import javax.annotation.Nullable;
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES; import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
import static org.briarproject.bramble.api.sync.RecordTypes.ACK; import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
import static org.briarproject.bramble.api.sync.RecordTypes.OFFER; import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST; import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS; import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_SUPPORTED_VERSIONS;
import static org.briarproject.bramble.api.sync.SyncConstants.PRIORITY_NONCE_BYTES;
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@@ -123,31 +119,6 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
reader.readVersions(); reader.readVersions();
} }
@Test(expected = FormatException.class)
public void testFormatExceptionIfPriorityNonceIsTooSmall()
throws Exception {
expectReadRecord(createPriority(PRIORITY_NONCE_BYTES - 1));
reader.readPriority();
}
@Test(expected = FormatException.class)
public void testFormatExceptionIfPriorityNonceIsTooLarge()
throws Exception {
expectReadRecord(createPriority(PRIORITY_NONCE_BYTES + 1));
reader.readPriority();
}
@Test
public void testNoFormatExceptionIfPriorityNonceIsCorrectSize()
throws Exception {
expectReadRecord(createPriority(PRIORITY_NONCE_BYTES));
Priority priority = reader.readPriority();
assertEquals(PRIORITY_NONCE_BYTES, priority.getNonce().length);
}
@Test @Test
public void testEofReturnsTrueWhenAtEndOfStream() throws Exception { public void testEofReturnsTrueWhenAtEndOfStream() throws Exception {
expectReadRecord(createAck()); expectReadRecord(createAck());
@@ -202,11 +173,6 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
return new Record(PROTOCOL_VERSION, VERSIONS, payload); return new Record(PROTOCOL_VERSION, VERSIONS, payload);
} }
private Record createPriority(int nonceBytes) {
byte[] payload = getRandomBytes(nonceBytes);
return new Record(PROTOCOL_VERSION, PRIORITY, payload);
}
private byte[] createPayload() throws Exception { private byte[] createPayload() throws Exception {
ByteArrayOutputStream payload = new ByteArrayOutputStream(); ByteArrayOutputStream payload = new ByteArrayOutputStream();
while (payload.size() + UniqueId.LENGTH <= MAX_RECORD_PAYLOAD_BYTES) { while (payload.size() + UniqueId.LENGTH <= MAX_RECORD_PAYLOAD_BYTES) {

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -38,11 +37,6 @@ public class TestDuplexTransportConnection
return writer; return writer;
} }
@Override
public TransportProperties getRemoteProperties() {
return new TransportProperties();
}
/** /**
* Creates and returns a pair of TestDuplexTransportConnections that are * Creates and returns a pair of TestDuplexTransportConnections that are
* connected to each other. * connected to each other.

View File

@@ -10,15 +10,12 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -88,12 +85,6 @@ public class TestPluginConfigModule {
public boolean shouldPoll() { public boolean shouldPoll() {
return false; return false;
} }
@Override
public Map<TransportId, List<TransportId>> getTransportPreferences() {
return emptyMap();
}
}; };
return pluginConfig; return pluginConfig;
} }

View File

@@ -16,7 +16,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: '*.jar') implementation fileTree(dir: 'libs', include: '*.jar')
implementation 'net.java.dev.jna:jna:4.5.2' implementation 'net.java.dev.jna:jna:4.5.2'
implementation 'net.java.dev.jna:jna-platform:4.5.2' implementation 'net.java.dev.jna:jna-platform:4.5.2'
tor 'org.briarproject:tor:0.3.5.10@zip' tor 'org.briarproject:tor:0.3.5.9@zip'
tor 'org.briarproject:obfs4proxy:0.0.7@zip' tor 'org.briarproject:obfs4proxy:0.0.7@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24' annotationProcessor 'com.google.dagger:dagger-compiler:2.24'

View File

@@ -1,15 +1,11 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.ShutdownManager; import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory; import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory;
@@ -20,8 +16,6 @@ import org.briarproject.bramble.plugin.tcp.WanTcpPluginFactory;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import dagger.Module; import dagger.Module;
@@ -29,8 +23,6 @@ import dagger.Provides;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
@Module @Module
public class DesktopPluginModule extends PluginModule { public class DesktopPluginModule extends PluginModule {
@@ -39,10 +31,10 @@ public class DesktopPluginModule extends PluginModule {
PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor, PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor,
SecureRandom random, BackoffFactory backoffFactory, SecureRandom random, BackoffFactory backoffFactory,
ReliabilityLayerFactory reliabilityFactory, ReliabilityLayerFactory reliabilityFactory,
ShutdownManager shutdownManager, EventBus eventBus, ShutdownManager shutdownManager, EventBus eventBus) {
TimeoutMonitor timeoutMonitor) { DuplexPluginFactory bluetooth =
DuplexPluginFactory bluetooth = new JavaBluetoothPluginFactory( new JavaBluetoothPluginFactory(ioExecutor, random, eventBus,
ioExecutor, random, eventBus, timeoutMonitor, backoffFactory); backoffFactory);
DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor, DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor,
reliabilityFactory); reliabilityFactory);
DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor, DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor,
@@ -68,13 +60,6 @@ public class DesktopPluginModule extends PluginModule {
public boolean shouldPoll() { public boolean shouldPoll() {
return true; return true;
} }
@Override
public Map<TransportId, List<TransportId>> getTransportPreferences() {
// Prefer LAN to Bluetooth
return singletonMap(BluetoothConstants.ID,
singletonList(LanTcpConstants.ID));
}
}; };
return pluginConfig; return pluginConfig;
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
@@ -35,11 +34,10 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
private volatile LocalDevice localDevice = null; private volatile LocalDevice localDevice = null;
JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager, JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager,
TimeoutMonitor timeoutMonitor, Executor ioExecutor, Executor ioExecutor, SecureRandom secureRandom,
SecureRandom secureRandom, Backoff backoff, Backoff backoff, PluginCallback callback, int maxLatency) {
PluginCallback callback, int maxLatency, int maxIdleTime) { super(connectionManager, ioExecutor, secureRandom, backoff, callback,
super(connectionManager, timeoutMonitor, ioExecutor, secureRandom, maxLatency);
backoff, callback, maxLatency, maxIdleTime);
} }
@Override @Override
@@ -121,9 +119,7 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
return "btspp://" + address + ":" + uuid + ";name=RFCOMM"; return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
} }
private DuplexTransportConnection wrapSocket(StreamConnection s) private DuplexTransportConnection wrapSocket(StreamConnection s) {
throws IOException { return new JavaBluetoothTransportConnection(this, connectionLimiter, s);
return new JavaBluetoothTransportConnection(this, connectionLimiter,
timeoutMonitor, s);
} }
} }

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -22,25 +21,22 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
public class JavaBluetoothPluginFactory implements DuplexPluginFactory { public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds 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 MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2; private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor; private final Executor ioExecutor;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final EventBus eventBus;
private final TimeoutMonitor timeoutMonitor;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
private final EventBus eventBus;
public JavaBluetoothPluginFactory(Executor ioExecutor, public JavaBluetoothPluginFactory(Executor ioExecutor,
SecureRandom secureRandom, EventBus eventBus, SecureRandom secureRandom, EventBus eventBus,
TimeoutMonitor timeoutMonitor, BackoffFactory backoffFactory) { BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.eventBus = eventBus;
this.timeoutMonitor = timeoutMonitor;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
this.eventBus = eventBus;
} }
@Override @Override
@@ -60,8 +56,7 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter, JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
timeoutMonitor, ioExecutor, secureRandom, backoff, callback, ioExecutor, secureRandom, backoff, callback, MAX_LATENCY);
MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
@@ -15,24 +14,20 @@ import javax.microedition.io.StreamConnection;
class JavaBluetoothTransportConnection class JavaBluetoothTransportConnection
extends AbstractDuplexTransportConnection { extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionLimiter; private final BluetoothConnectionLimiter connectionManager;
private final StreamConnection stream; private final StreamConnection stream;
private final InputStream in;
JavaBluetoothTransportConnection(Plugin plugin, JavaBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionLimiter, BluetoothConnectionLimiter connectionManager,
TimeoutMonitor timeoutMonitor, StreamConnection stream) {
StreamConnection stream) throws IOException {
super(plugin); super(plugin);
this.connectionLimiter = connectionLimiter;
this.stream = stream; this.stream = stream;
in = timeoutMonitor.createTimeoutInputStream( this.connectionManager = connectionManager;
stream.openInputStream(), plugin.getMaxIdleTime() * 2);
} }
@Override @Override
protected InputStream getInputStream() { protected InputStream getInputStream() throws IOException {
return in; return stream.openInputStream();
} }
@Override @Override
@@ -45,7 +40,7 @@ class JavaBluetoothTransportConnection
try { try {
stream.close(); stream.close();
} finally { } finally {
connectionLimiter.connectionClosed(this); connectionManager.connectionClosed(this);
} }
} }
} }

View File

@@ -24,7 +24,7 @@ dependencyVerification {
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8', '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.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.briarproject:obfs4proxy:0.0.7:obfs4proxy-0.0.7.zip:5b2f693262ce43a7e130f7cc7d5d1617925330640a2eb6d71085e95df8ee0642', 'org.briarproject:obfs4proxy:0.0.7:obfs4proxy-0.0.7.zip:5b2f693262ce43a7e130f7cc7d5d1617925330640a2eb6d71085e95df8ee0642',
'org.briarproject:tor:0.3.5.10:tor-0.3.5.10.zip:7b387d3523ae8af289c23be59dc4c64ec5d3721385d7825a09705095e3318d5c', 'org.briarproject:tor:0.3.5.9:tor-0.3.5.9.zip:6c3994b129db019cc23caaf50d6b4383903c40d05fbc47fc94211170a3e5d38c',
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a', 'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53', 'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',

View File

@@ -22,8 +22,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 28
versionCode 10207 versionCode 10205
versionName "1.2.7" versionName "1.2.5"
applicationId "org.briarproject.briar.android" applicationId "org.briarproject.briar.android"
buildConfigField "String", "GitHash", buildConfigField "String", "GitHash",
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\"" "\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
@@ -98,7 +98,7 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.1.0-beta01' implementation 'com.google.android.material:material:1.1.0-beta01'
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0-rc01' implementation 'androidx.recyclerview:recyclerview-selection:1.0.0'
implementation 'ch.acra:acra:4.11' implementation 'ch.acra:acra:4.11'
implementation 'info.guardianproject.panic:panic:1.0' implementation 'info.guardianproject.panic:panic:1.0'

View File

@@ -13,8 +13,9 @@ import java.util.Random;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import static androidx.test.InstrumentationRegistry.getContext; import static androidx.test.InstrumentationRegistry.getContext;
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
import static org.briarproject.briar.android.attachment.AttachmentItem.State.ERROR;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@@ -27,7 +28,7 @@ public class AttachmentRetrieverIntegrationTest {
private final ImageHelper imageHelper = new ImageHelperImpl(); private final ImageHelper imageHelper = new ImageHelperImpl();
private final AttachmentRetriever retriever = private final AttachmentRetriever retriever =
new AttachmentRetrieverImpl(null, dimensions, imageHelper, new AttachmentRetrieverImpl(null, null, dimensions, imageHelper,
new ImageSizeCalculator(imageHelper)); new ImageSizeCalculator(imageHelper));
@Test @Test
@@ -35,7 +36,7 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("kitten_small.jpg"); InputStream is = getAssetInputStream("kitten_small.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(msgId, item.getMessageId()); assertEquals(msgId, item.getMessageId());
assertEquals(160, item.getWidth()); assertEquals(160, item.getWidth());
assertEquals(240, item.getHeight()); assertEquals(240, item.getHeight());
@@ -43,7 +44,7 @@ public class AttachmentRetrieverIntegrationTest {
assertEquals(240, item.getThumbnailHeight()); assertEquals(240, item.getThumbnailHeight());
assertEquals("image/jpeg", item.getMimeType()); assertEquals("image/jpeg", item.getMimeType());
assertJpgOrJpeg(item.getExtension()); assertJpgOrJpeg(item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -51,7 +52,7 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("kitten_original.jpg"); InputStream is = getAssetInputStream("kitten_original.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(msgId, item.getMessageId()); assertEquals(msgId, item.getMessageId());
assertEquals(1728, item.getWidth()); assertEquals(1728, item.getWidth());
assertEquals(2592, item.getHeight()); assertEquals(2592, item.getHeight());
@@ -59,7 +60,7 @@ public class AttachmentRetrieverIntegrationTest {
assertEquals(dimensions.maxHeight, item.getThumbnailHeight()); assertEquals(dimensions.maxHeight, item.getThumbnailHeight());
assertEquals("image/jpeg", item.getMimeType()); assertEquals("image/jpeg", item.getMimeType());
assertJpgOrJpeg(item.getExtension()); assertJpgOrJpeg(item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -67,7 +68,7 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/png"); AttachmentHeader h = new AttachmentHeader(msgId, "image/png");
InputStream is = getAssetInputStream("kitten.png"); InputStream is = getAssetInputStream("kitten.png");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(msgId, item.getMessageId()); assertEquals(msgId, item.getMessageId());
assertEquals(737, item.getWidth()); assertEquals(737, item.getWidth());
assertEquals(510, item.getHeight()); assertEquals(510, item.getHeight());
@@ -75,7 +76,7 @@ public class AttachmentRetrieverIntegrationTest {
assertEquals(138, item.getThumbnailHeight()); assertEquals(138, item.getThumbnailHeight());
assertEquals("image/png", item.getMimeType()); assertEquals("image/png", item.getMimeType());
assertEquals("png", item.getExtension()); assertEquals("png", item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -83,14 +84,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("uber.gif"); InputStream is = getAssetInputStream("uber.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(1, item.getWidth()); assertEquals(1, item.getWidth());
assertEquals(1, item.getHeight()); assertEquals(1, item.getHeight());
assertEquals(dimensions.minHeight, item.getThumbnailWidth()); assertEquals(dimensions.minHeight, item.getThumbnailWidth());
assertEquals(dimensions.minHeight, item.getThumbnailHeight()); assertEquals(dimensions.minHeight, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -98,14 +99,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("lottapixel.jpg"); InputStream is = getAssetInputStream("lottapixel.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(64250, item.getWidth()); assertEquals(64250, item.getWidth());
assertEquals(64250, item.getHeight()); assertEquals(64250, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
assertEquals("image/jpeg", item.getMimeType()); assertEquals("image/jpeg", item.getMimeType());
assertJpgOrJpeg(item.getExtension()); assertJpgOrJpeg(item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -113,14 +114,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/png"); AttachmentHeader h = new AttachmentHeader(msgId, "image/png");
InputStream is = getAssetInputStream("image_io_crash.png"); InputStream is = getAssetInputStream("image_io_crash.png");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(1184, item.getWidth()); assertEquals(1184, item.getWidth());
assertEquals(448, item.getHeight()); assertEquals(448, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.minHeight, item.getThumbnailHeight()); assertEquals(dimensions.minHeight, item.getThumbnailHeight());
assertEquals("image/png", item.getMimeType()); assertEquals("image/png", item.getMimeType());
assertEquals("png", item.getExtension()); assertEquals("png", item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -128,14 +129,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("gimp_crash.gif"); InputStream is = getAssetInputStream("gimp_crash.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(1, item.getWidth()); assertEquals(1, item.getWidth());
assertEquals(1, item.getHeight()); assertEquals(1, item.getHeight());
assertEquals(dimensions.minHeight, item.getThumbnailWidth()); assertEquals(dimensions.minHeight, item.getThumbnailWidth());
assertEquals(dimensions.minHeight, item.getThumbnailHeight()); assertEquals(dimensions.minHeight, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -143,14 +144,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("opti_png_afl.gif"); InputStream is = getAssetInputStream("opti_png_afl.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(32, item.getWidth()); assertEquals(32, item.getWidth());
assertEquals(32, item.getHeight()); assertEquals(32, item.getHeight());
assertEquals(dimensions.minHeight, item.getThumbnailWidth()); assertEquals(dimensions.minHeight, item.getThumbnailWidth());
assertEquals(dimensions.minHeight, item.getThumbnailHeight()); assertEquals(dimensions.minHeight, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -158,8 +159,8 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("libraw_error.jpg"); InputStream is = getAssetInputStream("libraw_error.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertTrue(item.hasError()); assertEquals(ERROR, item.getState());
} }
@Test @Test
@@ -167,14 +168,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("animated.gif"); InputStream is = getAssetInputStream("animated.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(65535, item.getWidth()); assertEquals(65535, item.getWidth());
assertEquals(65535, item.getHeight()); assertEquals(65535, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -182,14 +183,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("animated2.gif"); InputStream is = getAssetInputStream("animated2.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(10000, item.getWidth()); assertEquals(10000, item.getWidth());
assertEquals(10000, item.getHeight()); assertEquals(10000, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -197,14 +198,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
InputStream is = getAssetInputStream("error_large.gif"); InputStream is = getAssetInputStream("error_large.gif");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(16384, item.getWidth()); assertEquals(16384, item.getWidth());
assertEquals(16384, item.getHeight()); assertEquals(16384, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
assertEquals("image/gif", item.getMimeType()); assertEquals("image/gif", item.getMimeType());
assertEquals("gif", item.getExtension()); assertEquals("gif", item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -212,14 +213,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("error_high.jpg"); InputStream is = getAssetInputStream("error_high.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(1, item.getWidth()); assertEquals(1, item.getWidth());
assertEquals(10000, item.getHeight()); assertEquals(10000, item.getHeight());
assertEquals(dimensions.minWidth, item.getThumbnailWidth()); assertEquals(dimensions.minWidth, item.getThumbnailWidth());
assertEquals(dimensions.maxHeight, item.getThumbnailHeight()); assertEquals(dimensions.maxHeight, item.getThumbnailHeight());
assertEquals("image/jpeg", item.getMimeType()); assertEquals("image/jpeg", item.getMimeType());
assertJpgOrJpeg(item.getExtension()); assertJpgOrJpeg(item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
@Test @Test
@@ -227,14 +228,14 @@ public class AttachmentRetrieverIntegrationTest {
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
InputStream is = getAssetInputStream("error_wide.jpg"); InputStream is = getAssetInputStream("error_wide.jpg");
Attachment a = new Attachment(h, is); Attachment a = new Attachment(h, is);
AttachmentItem item = retriever.getAttachmentItem(a, true); AttachmentItem item = retriever.createAttachmentItem(a, true);
assertEquals(1920, item.getWidth()); assertEquals(1920, item.getWidth());
assertEquals(1, item.getHeight()); assertEquals(1, item.getHeight());
assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
assertEquals(dimensions.minHeight, item.getThumbnailHeight()); assertEquals(dimensions.minHeight, item.getThumbnailHeight());
assertEquals("image/jpeg", item.getMimeType()); assertEquals("image/jpeg", item.getMimeType());
assertJpgOrJpeg(item.getExtension()); assertJpgOrJpeg(item.getExtension());
assertFalse(item.hasError()); assertEquals(AVAILABLE, item.getState());
} }
private InputStream getAssetInputStream(String name) throws Exception { private InputStream getAssetInputStream(String name) throws Exception {

View File

@@ -3,7 +3,7 @@ package org.briarproject.briar.android;
import android.app.Activity; import android.app.Activity;
import android.util.Log; import android.util.Log;
import org.briarproject.bramble.api.connection.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.test.TestDataCreator; import org.briarproject.briar.api.test.TestDataCreator;
import org.junit.ClassRule; import org.junit.ClassRule;

View File

@@ -120,8 +120,7 @@ public class SetupDataTest extends ScreenshotTest {
// TODO add messages // TODO add messages
connectionRegistry.registerConnection(bob.getId(), ID, () -> { connectionRegistry.registerConnection(bob.getId(), ID, true);
}, true);
} }
} }

View File

@@ -7,7 +7,6 @@ import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.account.BriarAccountModule; import org.briarproject.bramble.account.BriarAccountModule;
import org.briarproject.bramble.api.FeatureFlags; import org.briarproject.bramble.api.FeatureFlags;
import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactExchangeManager; import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.CryptoExecutor; import org.briarproject.bramble.api.crypto.CryptoExecutor;
@@ -20,6 +19,7 @@ import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.keyagreement.PayloadParser; import org.briarproject.bramble.api.keyagreement.PayloadParser;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;

View File

@@ -28,9 +28,7 @@ import static android.security.keystore.KeyProperties.PURPOSE_SIGN;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
@RequiresApi(23) @RequiresApi(23)
@NotNullByDefault @NotNullByDefault
@@ -81,10 +79,7 @@ class AndroidKeyStrengthener implements KeyStrengthener {
return true; return true;
} }
return false; return false;
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException | IOException e) {
logException(LOG, WARNING, e);
return false;
} catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }

Some files were not shown because too many files have changed in this diff Show More