Compare commits

..

1 Commits

Author SHA1 Message Date
akwizgran
4a4d336126 Disable image attachments for remote contacts alpha. 2019-06-10 17:47:48 +01:00
833 changed files with 7130 additions and 20232 deletions

View File

@@ -17,7 +17,7 @@ test:
script: script:
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest - ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom check compileOfficialDebugAndroidTestSources compileScreenshotDebugAndroidTestSources - ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom test
after_script: after_script:
# these file change every time but should not be cached # these file change every time but should not be cached

View File

@@ -1,10 +1,16 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="100" />
<AndroidXmlCodeStyleSettings> <AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" /> <option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings> </AndroidXmlCodeStyleSettings>
<JavaCodeStyleSettings> <JavaCodeStyleSettings>
<option name="ANNOTATION_PARAMETER_WRAP" value="1" /> <option name="ANNOTATION_PARAMETER_WRAP" value="1" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE"> <option name="IMPORT_LAYOUT_TABLE">
<value> <value>
<package name="android" withSubpackages="true" static="false" /> <package name="android" withSubpackages="true" static="false" />
@@ -71,6 +77,7 @@
</indentOptions> </indentOptions>
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="XML"> <codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions> <indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" /> <option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" /> <option name="USE_TAB_CHARACTER" value="true" />
@@ -83,8 +90,7 @@
<match> <match>
<AND> <AND>
<NAME>xmlns:android</NAME> <NAME>xmlns:android</NAME>
<XML_ATTRIBUTE /> <XML_NAMESPACE>Namespace:</XML_NAMESPACE>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
</rule> </rule>
@@ -94,8 +100,7 @@
<match> <match>
<AND> <AND>
<NAME>xmlns:.*</NAME> <NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE /> <XML_NAMESPACE>Namespace:</XML_NAMESPACE>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
<order>BY_NAME</order> <order>BY_NAME</order>
@@ -106,7 +111,6 @@
<match> <match>
<AND> <AND>
<NAME>.*:id</NAME> <NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@@ -117,7 +121,6 @@
<match> <match>
<AND> <AND>
<NAME>.*:name</NAME> <NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@@ -128,7 +131,6 @@
<match> <match>
<AND> <AND>
<NAME>name</NAME> <NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE> <XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@@ -139,7 +141,6 @@
<match> <match>
<AND> <AND>
<NAME>style</NAME> <NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE> <XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@@ -150,7 +151,6 @@
<match> <match>
<AND> <AND>
<NAME>.*</NAME> <NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE> <XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@@ -161,12 +161,64 @@
<rule> <rule>
<match> <match>
<AND> <AND>
<NAME>.*</NAME> <NAME>.*:layout_width</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND> </AND>
</match> </match>
<order>ANDROID_ATTRIBUTE_ORDER</order> </rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule> </rule>
</section> </section>
<section> <section>
@@ -174,7 +226,6 @@
<match> <match>
<AND> <AND>
<NAME>.*</NAME> <NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE> <XML_NAMESPACE>.*</XML_NAMESPACE>
</AND> </AND>
</match> </match>

View File

@@ -5,31 +5,23 @@ apply plugin: 'witness'
apply from: 'witness.gradle' apply from: 'witness.gradle'
android { android {
compileSdkVersion 29 compileSdkVersion 28
buildToolsVersion '29.0.2' buildToolsVersion '28.0.3'
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 14
targetSdkVersion 28 targetSdkVersion 26
versionCode 10207 versionCode 10107
versionName "1.2.7" versionName "1.1.7"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
lintOptions {
// FIXME
warning "LintError"
warning "InvalidPackage"
warning "MissingPermission"
warning "InlinedApi", "ObsoleteSdkInt", "Override", "NewApi", "UnusedAttribute"
}
} }
configurations { configurations {
@@ -38,10 +30,10 @@ 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.9@zip' tor 'org.briarproject:tor-android:0.3.5.8@zip'
tor 'org.briarproject:obfs4proxy-android:0.0.11-2@zip' tor 'org.briarproject:obfs4proxy-android:0.0.9@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24' annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
compileOnly 'javax.annotation:jsr250-api:1.0' compileOnly 'javax.annotation:jsr250-api:1.0'
@@ -67,8 +59,6 @@ task unpackTorBinaries {
copy { copy {
from configurations.tor.collect { zipTree(it) } from configurations.tor.collect { zipTree(it) }
into torBinariesDir into torBinariesDir
// TODO: Remove after next Tor upgrade, which won't include non-PIE binaries
include 'geoip.zip', '*_pie.zip'
} }
} }
dependsOn cleanTorBinaries dependsOn cleanTorBinaries

View File

@@ -11,14 +11,4 @@ public interface BrambleAndroidEagerSingletons {
void inject(AndroidNetworkModule.EagerSingletons init); void inject(AndroidNetworkModule.EagerSingletons init);
void inject(ReportingModule.EagerSingletons init); void inject(ReportingModule.EagerSingletons init);
class Helper {
public static void injectEagerSingletons(
BrambleAndroidEagerSingletons c) {
c.inject(new AndroidBatteryModule.EagerSingletons());
c.inject(new AndroidNetworkModule.EagerSingletons());
c.inject(new ReportingModule.EagerSingletons());
}
}
} }

View File

@@ -18,4 +18,10 @@ import dagger.Module;
SocksModule.class SocksModule.class
}) })
public class BrambleAndroidModule { public class BrambleAndroidModule {
public static void initEagerSingletons(BrambleAndroidEagerSingletons c) {
c.inject(new AndroidBatteryModule.EagerSingletons());
c.inject(new AndroidNetworkModule.EagerSingletons());
c.inject(new ReportingModule.EagerSingletons());
}
} }

View File

@@ -12,16 +12,13 @@ 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;
import javax.annotation.Nullable; import javax.annotation.Nullable;
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,11 +29,7 @@ class AndroidAccountManager extends AccountManagerImpl
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(AndroidAccountManager.class.getName()); Logger.getLogger(AndroidAccountManager.class.getName());
/** private static final String PREF_DB_KEY = "key";
* 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;
@@ -60,6 +53,36 @@ class AndroidAccountManager extends AccountManagerImpl
return exists; return exists;
} }
// Locking: stateChangeLock
@Override
@Nullable
protected String loadEncryptedDatabaseKey() {
String key = getDatabaseKeyFromPreferences();
if (key == null) key = super.loadEncryptedDatabaseKey();
else migrateDatabaseKeyToFile(key);
return key;
}
// Locking: stateChangeLock
@Nullable
private String getDatabaseKeyFromPreferences() {
String key = prefs.getString(PREF_DB_KEY, null);
if (key == null) LOG.info("No database key in preferences");
else LOG.info("Found database key in preferences");
return key;
}
// Locking: stateChangeLock
private void migrateDatabaseKeyToFile(String key) {
if (storeEncryptedDatabaseKey(key)) {
if (prefs.edit().remove(PREF_DB_KEY).commit())
LOG.info("Database key migrated to file");
else LOG.warning("Database key not removed from preferences");
} else {
LOG.warning("Database key not migrated to file");
}
}
@Override @Override
public void deleteAccount() { public void deleteAccount() {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
@@ -82,14 +105,14 @@ class AndroidAccountManager extends AccountManagerImpl
return PreferenceManager.getDefaultSharedPreferences(appContext); return PreferenceManager.getDefaultSharedPreferences(appContext);
} }
@GuardedBy("stateChangeLock") // Locking: stateChangeLock
private void deleteAppData(SharedPreferences... clear) { private void deleteAppData(SharedPreferences... clear) {
// Clear and commit shared preferences // Clear and commit shared preferences
for (SharedPreferences prefs : clear) { for (SharedPreferences prefs : clear) {
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 +121,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 +140,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

@@ -8,34 +8,24 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.os.ParcelUuid;
import android.os.Parcelable;
import org.briarproject.bramble.api.Pair;
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;
import org.briarproject.bramble.api.plugin.DiscoveryHandler;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginException; import org.briarproject.bramble.api.plugin.PluginException;
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.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@@ -57,25 +47,12 @@ 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.ACTION_UUID;
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.bluetooth.BluetoothDevice.EXTRA_UUID;
import static android.os.Build.VERSION.SDK_INT;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
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;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -84,9 +61,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
private static final Logger LOG = private static final Logger LOG =
getLogger(AndroidBluetoothPlugin.class.getName()); getLogger(AndroidBluetoothPlugin.class.getName());
private static final int MIN_DEVICE_DISCOVERY_MS = 2_000; private static final int MAX_DISCOVERY_MS = 10_000;
private static final int MAX_DEVICE_DISCOVERY_MS = 30_000;
private static final int MAX_SERVICE_DISCOVERY_MS = 15_000;
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final Context appContext; private final Context appContext;
@@ -160,42 +135,17 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
@Override @Override
void disableAdapterIfEnabledByUs() { void disableAdapterIfEnabledByUs() {
if (isAdapterEnabled() && wasEnabledByUs) { if (isAdapterEnabled() && wasEnabledByUs) {
cancelDiscoverability();
if (adapter.disable()) LOG.info("Disabling Bluetooth"); if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth"); else LOG.info("Could not disable Bluetooth");
wasEnabledByUs = false; wasEnabledByUs = false;
} }
} }
private void cancelDiscoverability() {
if (adapter.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
try {
Method setDiscoverableTimeout = BluetoothAdapter.class
.getDeclaredMethod("setDiscoverableTimeout", int.class);
setDiscoverableTimeout.setAccessible(true);
setDiscoverableTimeout.invoke(adapter, 1);
LOG.info("Cancelled discoverability");
} catch (NoSuchMethodException e) {
logException(LOG, WARNING, e);
} catch (IllegalAccessException e) {
logException(LOG, WARNING, e);
} catch (InvocationTargetException e) {
logException(LOG, WARNING, e);
}
}
}
@Override @Override
void setEnabledByUs() { void setEnabledByUs() {
wasEnabledByUs = true; wasEnabledByUs = true;
} }
@Override
void onAdapterDisabled() {
super.onAdapterDisabled();
wasEnabledByUs = false;
}
@Override @Override
@Nullable @Nullable
String getBluetoothAddress() { String getBluetoothAddress() {
@@ -250,8 +200,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
@Nullable @Nullable
DuplexTransportConnection discoverAndConnect(String uuid) { DuplexTransportConnection discoverAndConnect(String uuid) {
if (adapter == null) return null; if (adapter == null) return null;
for (BluetoothDevice d : discoverDevices()) { for (String address : discoverDevices()) {
String address = d.getAddress();
try { try {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubMacAddress(address)); LOG.info("Connecting to " + scrubMacAddress(address));
@@ -267,184 +216,10 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
return null; return null;
} }
@Override private Collection<String> discoverDevices() {
public boolean supportsDiscovery() { List<String> addresses = new ArrayList<>();
return true;
}
@Override
public void discoverPeers(
List<Pair<TransportProperties, DiscoveryHandler>> properties) {
// Discover all nearby devices
List<BluetoothDevice> devices = discoverDevices();
if (devices.isEmpty()) {
LOG.info("No devices discovered");
return;
}
List<Pair<TransportProperties, DiscoveryHandler>> discovered =
new ArrayList<>();
Map<String, Pair<TransportProperties, DiscoveryHandler>> byUuid =
new HashMap<>();
Map<String, Pair<TransportProperties, DiscoveryHandler>> byAddress =
new HashMap<>();
for (Pair<TransportProperties, DiscoveryHandler> pair : properties) {
TransportProperties p = pair.getFirst();
String uuid = p.get(PROP_UUID);
if (!isNullOrEmpty(uuid)) {
byUuid.put(uuid, pair);
String address = p.get(PROP_ADDRESS);
if (!isNullOrEmpty(address)) byAddress.put(address, pair);
}
}
List<BluetoothDevice> unknown = new ArrayList<>(devices);
for (BluetoothDevice d : devices) {
Pair<TransportProperties, DiscoveryHandler> pair =
byAddress.remove(d.getAddress());
if (pair == null) {
// Try cached UUIDs
for (String uuid : getUuids(d)) {
pair = byUuid.remove(uuid);
if (pair != null) {
if (LOG.isLoggable(INFO)) {
LOG.info("Matched "
+ scrubMacAddress(d.getAddress())
+ " by cached UUID");
}
TransportProperties p =
new TransportProperties(pair.getFirst());
p.put(PROP_ADDRESS, d.getAddress());
discovered.add(new Pair<>(p, pair.getSecond()));
unknown.remove(d);
break;
}
}
} else {
if (LOG.isLoggable(INFO)) {
LOG.info("Matched " + scrubMacAddress(d.getAddress())
+ " by address");
}
discovered.add(pair);
unknown.remove(d);
}
}
if (unknown.isEmpty()) {
LOG.info("All discovered devices are known, not fetching UUIDs");
return;
}
// Fetch up-to-date UUIDs
if (LOG.isLoggable(INFO))
LOG.info("Fetching UUIDs for " + unknown.size() + " devices");
BlockingQueue<Intent> intents = new LinkedBlockingQueue<>(); BlockingQueue<Intent> intents = new LinkedBlockingQueue<>();
QueueingReceiver receiver = new QueueingReceiver(intents); DiscoveryReceiver receiver = new DiscoveryReceiver(intents);
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_UUID);
appContext.registerReceiver(receiver, filter);
try {
List<BluetoothDevice> pending = new ArrayList<>();
for (BluetoothDevice d : unknown) {
if (d.fetchUuidsWithSdp()) {
if (LOG.isLoggable(INFO)) {
LOG.info("Fetching UUIDs for "
+ scrubMacAddress(d.getAddress()));
}
pending.add(d);
} else {
if (LOG.isLoggable(INFO)) {
LOG.info("Failed to fetch UUIDs for "
+ scrubMacAddress(d.getAddress()));
}
}
}
long now = clock.currentTimeMillis();
long end = now + MAX_SERVICE_DISCOVERY_MS;
while (now < end && !pending.isEmpty()) {
Intent i = intents.poll(end - now, MILLISECONDS);
if (i == null) break;
BluetoothDevice d = requireNonNull(
i.getParcelableExtra(EXTRA_DEVICE));
if (LOG.isLoggable(INFO)) {
LOG.info("Fetched UUIDs for "
+ scrubMacAddress(d.getAddress()));
}
Set<String> uuids = getUuids(d);
Parcelable[] extra = i.getParcelableArrayExtra(EXTRA_UUID);
if (extra != null) {
for (Parcelable p : extra) {
uuids.addAll(getUuidStrings((ParcelUuid) p));
}
}
for (String uuid : uuids) {
Pair<TransportProperties, DiscoveryHandler> pair =
byUuid.remove(uuid);
if (pair != null) {
if (LOG.isLoggable(INFO)) {
LOG.info("Matched "
+ scrubMacAddress(d.getAddress())
+ " by fetched UUID");
}
TransportProperties p =
new TransportProperties(pair.getFirst());
p.put(PROP_ADDRESS, d.getAddress());
discovered.add(new Pair<>(p, pair.getSecond()));
break;
}
}
pending.remove(d);
now = clock.currentTimeMillis();
}
if (LOG.isLoggable(INFO)) {
if (pending.isEmpty()) {
LOG.info("Finished fetching UUIDs");
} else {
LOG.info("Failed to fetch UUIDs for " + pending.size()
+ " devices");
}
}
} catch (InterruptedException e) {
LOG.info("Interrupted while fetching UUIDs");
Thread.currentThread().interrupt();
} finally {
appContext.unregisterReceiver(receiver);
}
if (LOG.isLoggable(INFO)) {
LOG.info("Discovered " + discovered.size() + " contacts");
}
for (Pair<TransportProperties, DiscoveryHandler> pair : discovered) {
pair.getSecond().handleDevice(pair.getFirst());
}
}
private Set<String> getUuids(BluetoothDevice d) {
Set<String> strings = new TreeSet<>();
ParcelUuid[] uuids = d.getUuids();
if (uuids == null) return strings;
for (ParcelUuid u : uuids) strings.addAll(getUuidStrings(u));
return strings;
}
// Workaround for https://code.google.com/p/android/issues/detail?id=197341
private List<String> getUuidStrings(ParcelUuid u) {
UUID forwards = u.getUuid();
ByteBuffer buf = ByteBuffer.allocate(16);
buf.putLong(forwards.getLeastSignificantBits());
buf.putLong(forwards.getMostSignificantBits());
buf.rewind();
buf.order(LITTLE_ENDIAN);
UUID backwards = new UUID(buf.getLong(), buf.getLong());
return asList(forwards.toString(), backwards.toString());
}
private List<BluetoothDevice> discoverDevices() {
if (adapter.isDiscovering()) {
LOG.info("Already discovering");
return emptyList();
}
List<BluetoothDevice> devices = new ArrayList<>();
BlockingQueue<Intent> intents = new LinkedBlockingQueue<>();
QueueingReceiver receiver = new QueueingReceiver(intents);
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_DISCOVERY_STARTED); filter.addAction(ACTION_DISCOVERY_STARTED);
filter.addAction(ACTION_DISCOVERY_FINISHED); filter.addAction(ACTION_DISCOVERY_FINISHED);
@@ -452,9 +227,8 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
appContext.registerReceiver(receiver, filter); appContext.registerReceiver(receiver, filter);
try { try {
if (adapter.startDiscovery()) { if (adapter.startDiscovery()) {
long start = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
long end = start + MAX_DEVICE_DISCOVERY_MS; long end = now + MAX_DISCOVERY_MS;
long now = start;
while (now < end) { while (now < end) {
Intent i = intents.poll(end - now, MILLISECONDS); Intent i = intents.poll(end - now, MILLISECONDS);
if (i == null) break; if (i == null) break;
@@ -463,27 +237,14 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
LOG.info("Discovery started"); LOG.info("Discovery started");
} else if (ACTION_DISCOVERY_FINISHED.equals(action)) { } else if (ACTION_DISCOVERY_FINISHED.equals(action)) {
LOG.info("Discovery finished"); LOG.info("Discovery finished");
now = clock.currentTimeMillis(); break;
if (now - start < MIN_DEVICE_DISCOVERY_MS) {
LOG.info("Discovery finished quickly, retrying");
if (!adapter.startDiscovery()) {
LOG.info("Could not restart discovery");
break;
}
} else {
break;
}
} else if (ACTION_FOUND.equals(action)) { } else if (ACTION_FOUND.equals(action)) {
BluetoothDevice d = requireNonNull( BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
i.getParcelableExtra(EXTRA_DEVICE)); String address = d.getAddress();
// Ignore Bluetooth LE devices if (LOG.isLoggable(INFO))
if (SDK_INT < 18 || d.getType() != DEVICE_TYPE_LE) { LOG.info("Discovered " + scrubMacAddress(address));
if (LOG.isLoggable(INFO)) { if (!addresses.contains(address))
LOG.info("Discovered " addresses.add(address);
+ scrubMacAddress(d.getAddress()));
}
if (!devices.contains(d)) devices.add(d);
}
} }
now = clock.currentTimeMillis(); now = clock.currentTimeMillis();
} }
@@ -498,9 +259,9 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
adapter.cancelDiscovery(); adapter.cancelDiscovery();
appContext.unregisterReceiver(receiver); appContext.unregisterReceiver(receiver);
} }
// Shuffle the devices so we don't always try the same one first // Shuffle the addresses so we don't always try the same one first
shuffle(devices); Collections.shuffle(addresses);
return devices; return addresses;
} }
private class BluetoothStateReceiver extends BroadcastReceiver { private class BluetoothStateReceiver extends BroadcastReceiver {
@@ -521,11 +282,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
} }
} }
private static class QueueingReceiver extends BroadcastReceiver { private static class DiscoveryReceiver extends BroadcastReceiver {
private final BlockingQueue<Intent> intents; private final BlockingQueue<Intent> intents;
private QueueingReceiver(BlockingQueue<Intent> intents) { private DiscoveryReceiver(BlockingQueue<Intent> intents) {
this.intents = intents; this.intents = intents;
} }

View File

@@ -9,17 +9,17 @@ import android.net.wifi.WifiManager;
import org.briarproject.bramble.PoliteExecutor; import org.briarproject.bramble.PoliteExecutor;
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.network.event.NetworkStatusEvent; import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
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.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.settings.Settings;
import java.io.IOException; 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;
@@ -32,18 +32,27 @@ import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
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.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
@NotNullByDefault @NotNullByDefault
class AndroidLanTcpPlugin extends LanTcpPlugin { 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
@@ -53,9 +62,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
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);
@@ -71,31 +79,32 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
@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;
Settings settings = callback.getSettings();
state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, false));
updateConnectionStatus(); updateConnectionStatus();
} }
@Override
public void stop() {
running = false;
tryToClose(socket);
}
@Override @Override
protected Socket createSocket() throws IOException { protected Socket createSocket() throws IOException {
return socketFactory.createSocket(); return socketFactory.createSocket();
} }
@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();
} }
@@ -129,38 +138,29 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
super.eventOccurred(e);
if (e instanceof NetworkStatusEvent) updateConnectionStatus(); if (e instanceof NetworkStatusEvent) updateConnectionStatus();
} }
private void updateConnectionStatus() { private void updateConnectionStatus() {
connectionStatusExecutor.execute(() -> { connectionStatusExecutor.execute(() -> {
State s = getState(); if (!running) return;
if (s != ACTIVE && s != INACTIVE) return; Collection<InetAddress> addrs = getLocalIpAddresses();
List<InetAddress> addrs = getLocalInetAddresses(); 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
// make outgoing connections on API 21+ if another network // make outgoing connections on API 21+ if another network
// has internet access // has internet access
socketFactory = SocketFactory.getDefault(); socketFactory = SocketFactory.getDefault();
if (s == INACTIVE) bind(); if (socket == null || socket.isClosed()) bind();
} else if (addrs.isEmpty()) { } else if (addrs.isEmpty()) {
LOG.info("Not connected to wifi"); LOG.info("Not connected to wifi");
socketFactory = SocketFactory.getDefault(); socketFactory = SocketFactory.getDefault();
// Server socket may not have been closed automatically when tryToClose(socket);
// interface was taken down. Socket will be cleared and state
// updated in acceptContactConnections()
if (s == ACTIVE) {
LOG.info("Closing server socket");
tryToClose(state.getServerSocket(), LOG, WARNING);
}
} else { } else {
LOG.info("Connected to wifi"); LOG.info("Connected to wifi");
socketFactory = getSocketFactory(); socketFactory = getSocketFactory();
if (s == INACTIVE) bind(); if (socket == null || socket.isClosed()) bind();
} }
}); });
} }

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

@@ -74,6 +74,7 @@ class AndroidTorPlugin extends TorPlugin {
@Override @Override
protected void enableNetwork(boolean enable) throws IOException { protected void enableNetwork(boolean enable) throws IOException {
if (!running) return;
if (enable) wakeLock.acquire(); if (enable) wakeLock.acquire();
super.enableNetwork(enable); super.enableNetwork(enable);
if (!enable) wakeLock.release(); if (!enable) wakeLock.release();

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.plugin.tor; package org.briarproject.bramble.plugin.tor;
import android.content.Context; import android.content.Context;
import android.os.Build;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
@@ -88,15 +89,9 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
// Check that we have a Tor binary for this architecture // Check that we have a Tor binary for this architecture
String architecture = null; String architecture = null;
for (String abi : AndroidUtils.getSupportedArchitectures()) { for (String abi : AndroidUtils.getSupportedArchitectures()) {
if (abi.startsWith("x86_64")) { if (abi.startsWith("x86")) {
architecture = "x86_64";
break;
} else if (abi.startsWith("x86")) {
architecture = "x86"; architecture = "x86";
break; break;
} else if (abi.startsWith("arm64")) {
architecture = "arm64";
break;
} else if (abi.startsWith("armeabi")) { } else if (abi.startsWith("armeabi")) {
architecture = "arm"; architecture = "arm";
break; break;
@@ -106,8 +101,8 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
LOG.info("Tor is not supported on this architecture"); LOG.info("Tor is not supported on this architecture");
return null; return null;
} }
// Use position-independent executable // Use position-independent executable for SDK >= 16
architecture += "_pie"; if (Build.VERSION.SDK_INT >= 16) architecture += "_pie";
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);

View File

@@ -61,12 +61,12 @@ class AndroidLocationUtils implements LocationUtils {
private String getCountryFromPhoneNetwork() { private String getCountryFromPhoneNetwork() {
Object o = appContext.getSystemService(TELEPHONY_SERVICE); Object o = appContext.getSystemService(TELEPHONY_SERVICE);
TelephonyManager tm = (TelephonyManager) o; TelephonyManager tm = (TelephonyManager) o;
return tm == null ? "" : tm.getNetworkCountryIso(); return tm.getNetworkCountryIso();
} }
private String getCountryFromSimCard() { private String getCountryFromSimCard() {
Object o = appContext.getSystemService(TELEPHONY_SERVICE); Object o = appContext.getSystemService(TELEPHONY_SERVICE);
TelephonyManager tm = (TelephonyManager) o; TelephonyManager tm = (TelephonyManager) o;
return tm == null ? "" : tm.getSimCountryIso(); return tm.getSimCountryIso();
} }
} }

View File

@@ -23,7 +23,6 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.content.Context.WIFI_SERVICE; import static android.content.Context.WIFI_SERVICE;
import static android.os.Build.VERSION.SDK_INT;
import static android.provider.Settings.Secure.ANDROID_ID; import static android.provider.Settings.Secure.ANDROID_ID;
@Immutable @Immutable
@@ -75,7 +74,8 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
// Silence strict mode // Silence strict mode
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites(); StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites();
super.writeSeed(); super.writeSeed();
if (SDK_INT <= 18) applyOpenSslFix(); if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18)
applyOpenSslFix();
StrictMode.setThreadPolicy(tp); StrictMode.setThreadPolicy(tp);
} }

View File

@@ -6,25 +6,15 @@ import android.content.Context;
import android.os.Build; import android.os.Build;
import android.provider.Settings; import android.provider.Settings;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File; import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import javax.annotation.Nullable;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static java.util.Arrays.asList;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@NotNullByDefault
public class AndroidUtils { public class AndroidUtils {
// Fake Bluetooth address returned by BluetoothAdapter on API 23 and later // Fake Bluetooth address returned by BluetoothAdapter on API 23 and later
@@ -32,10 +22,11 @@ public class AndroidUtils {
private static final String STORED_REPORTS = "dev-reports"; private static final String STORED_REPORTS = "dev-reports";
@SuppressWarnings("deprecation")
public static Collection<String> getSupportedArchitectures() { public static Collection<String> getSupportedArchitectures() {
List<String> abis = new ArrayList<>(); List<String> abis = new ArrayList<>();
if (SDK_INT >= 21) { if (SDK_INT >= 21) {
abis.addAll(asList(Build.SUPPORTED_ABIS)); abis.addAll(Arrays.asList(Build.SUPPORTED_ABIS));
} else { } else {
abis.add(Build.CPU_ABI); abis.add(Build.CPU_ABI);
if (Build.CPU_ABI2 != null) abis.add(Build.CPU_ABI2); if (Build.CPU_ABI2 != null) abis.add(Build.CPU_ABI2);
@@ -45,76 +36,25 @@ public class AndroidUtils {
public static String getBluetoothAddress(Context ctx, public static String getBluetoothAddress(Context ctx,
BluetoothAdapter adapter) { BluetoothAdapter adapter) {
return getBluetoothAddressAndMethod(ctx, adapter).getFirst();
}
public static Pair<String, String> getBluetoothAddressAndMethod(Context ctx,
BluetoothAdapter adapter) {
// Return the adapter's address if it's valid and not fake // Return the adapter's address if it's valid and not fake
@SuppressLint("HardwareIds") @SuppressLint("HardwareIds")
String address = adapter.getAddress(); String address = adapter.getAddress();
if (isValidBluetoothAddress(address)) { if (isValidBluetoothAddress(address)) return address;
return new Pair<>(address, "adapter");
}
// Return the address from settings if it's valid and not fake // Return the address from settings if it's valid and not fake
address = Settings.Secure.getString(ctx.getContentResolver(), address = Settings.Secure.getString(ctx.getContentResolver(),
"bluetooth_address"); "bluetooth_address");
if (isValidBluetoothAddress(address)) { if (isValidBluetoothAddress(address)) return address;
return new Pair<>(address, "settings");
}
// Try to get the address via reflection
address = getBluetoothAddressByReflection(adapter);
if (isValidBluetoothAddress(address)) {
return new Pair<>(requireNonNull(address), "reflection");
}
// Let the caller know we can't find the address // Let the caller know we can't find the address
return new Pair<>("", ""); return "";
} }
private static boolean isValidBluetoothAddress(@Nullable String address) { private static boolean isValidBluetoothAddress(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);
} }
@Nullable
private static String getBluetoothAddressByReflection(
BluetoothAdapter adapter) {
try {
Field mServiceField =
adapter.getClass().getDeclaredField("mService");
mServiceField.setAccessible(true);
Object mService = mServiceField.get(adapter);
// mService may be null when Bluetooth is disabled
if (mService == null) throw new NoSuchFieldException();
Method getAddressMethod =
mService.getClass().getMethod("getAddress");
return (String) getAddressMethod.invoke(mService);
} catch (NoSuchFieldException e) {
return null;
} catch (IllegalAccessException e) {
return null;
} catch (NoSuchMethodException e) {
return null;
} catch (InvocationTargetException e) {
return null;
} catch (SecurityException e) {
return null;
}
}
public static File getReportDir(Context ctx) { public static File getReportDir(Context ctx) {
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE); return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
} }
/**
* Returns an array of supported content types for image attachments.
* GIFs can't be compressed on API < 24 so they're not supported.
* <p>
* TODO: Remove this restriction when large message support is added
*/
public static String[] getSupportedImageContentTypes() {
if (SDK_INT < 24) return new String[] {"image/jpeg", "image/png"};
else return new String[] {"image/jpeg", "image/png", "image/gif"};
}
} }

View File

@@ -16,10 +16,13 @@ import org.junit.Test;
import java.io.File; import java.io.File;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.util.StringUtils.toHexString;
public class AndroidAccountManagerTest extends BrambleMockTestCase { public class AndroidAccountManagerTest extends BrambleMockTestCase {
@@ -37,8 +40,11 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
private final Application app; private final Application app;
private final ApplicationInfo applicationInfo; private final ApplicationInfo applicationInfo;
private final String encryptedKeyHex = toHexString(getRandomBytes(123));
private final File testDir = getTestDirectory(); private final File testDir = getTestDirectory();
private final File keyDir = new File(testDir, "key"); private final File keyDir = new File(testDir, "key");
private final File keyFile = new File(keyDir, "db.key");
private final File keyBackupFile = new File(keyDir, "db.key.bak");
private final File dbDir = new File(testDir, "db"); private final File dbDir = new File(testDir, "db");
private AndroidAccountManager accountManager; private AndroidAccountManager accountManager;
@@ -69,12 +75,33 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
}; };
} }
@Test
public void testDbKeyIsMigratedFromPreferencesToFile() {
context.checking(new Expectations() {{
oneOf(prefs).getString("key", null);
will(returnValue(encryptedKeyHex));
oneOf(prefs).edit();
will(returnValue(editor));
oneOf(editor).remove("key");
will(returnValue(editor));
oneOf(editor).commit();
will(returnValue(true));
}});
assertFalse(keyFile.exists());
assertFalse(keyBackupFile.exists());
assertEquals(encryptedKeyHex,
accountManager.loadEncryptedDatabaseKey());
assertTrue(keyFile.exists());
assertTrue(keyBackupFile.exists());
}
@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 +140,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 +155,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

@@ -1,46 +1,44 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.android.tools.analytics-library:protos:26.5.1:protos-26.5.1.jar:8dde1130725461fe827f2a343d353f2b51e8870661fc860d7d5ebddb097ead4e', 'com.android.tools.analytics-library:protos:26.4.0:protos-26.4.0.jar:ad760915586797d39319f402837b378bff3bb4ed583e3e0c48c965631fb2135f',
'com.android.tools.analytics-library:shared:26.5.1:shared-26.5.1.jar:ccc2f3b00ec17b11401610ba68553544fc8fc517120e84439ac6eb86b875e18d', 'com.android.tools.analytics-library:shared:26.4.0:shared-26.4.0.jar:1332106a905d48909c81268c9e414946de3e83487db394c6073b0a9b5c3d0ed2',
'com.android.tools.analytics-library:tracker:26.5.1:tracker-26.5.1.jar:3a76984c0fe2e847ca7a8b35b4780ef0447a9d1666946cb8e60466318e0ab5ae', 'com.android.tools.analytics-library:tracker:26.4.0:tracker-26.4.0.jar:d0020cfbfd4cd75935f2972d6a24089840d4a10df6f3ef2a796093217dd37796',
'com.android.tools.build:aapt2-proto:0.4.0:aapt2-proto-0.4.0.jar:fac0435e08898f89eeeb9ca236bea707155ff816c12205ced285ad53604133ca', 'com.android.tools.build:apksig:3.4.0:apksig-3.4.0.jar:91d5a1866139c69756280355a6f61b4d619d0516841580114f45a10f2177327e',
'com.android.tools.build:apksig:3.5.1:apksig-3.5.1.jar:1fd33e7f009a2a0da766cfeec4211a09f548034b015c289a66d75dd8a9302f4a', 'com.android.tools.build:apkzlib:3.4.0:apkzlib-3.4.0.jar:8653c85f5fdf1dde840e8b8af7396aeb79c34b66e541b5860059616006535592',
'com.android.tools.build:apkzlib:3.5.1:apkzlib-3.5.1.jar:9f330167cbe973b7db407692f74f4f6453b7ffa5f2048934b06280c2ceee60fa', 'com.android.tools.build:builder-model:3.4.0:builder-model-3.4.0.jar:a88f138124a9f016a70bcb4760359a502f65c7deed56507ee4014f4dd9ea853b',
'com.android.tools.build:builder-model:3.5.1:builder-model-3.5.1.jar:39ea3c82b76b6e0c9f9fa88d93e0edc1dd4a0f1dfae0ef6fbf2d451da47e5450', 'com.android.tools.build:builder-test-api:3.4.0:builder-test-api-3.4.0.jar:31089ab1ec19ca7687a010867d2f3807513c805b8226979706f4247b5d4df26f',
'com.android.tools.build:builder-test-api:3.5.1:builder-test-api-3.5.1.jar:a1b59305584cbcaa078fdc9cfb80871012755b822dd32e8da19add6f7bbcb762', 'com.android.tools.build:builder:3.4.0:builder-3.4.0.jar:476221b5203a7f50089bf185ed95000a34b6f5020ef0a17815afd58606922679',
'com.android.tools.build:builder:3.5.1:builder-3.5.1.jar:e3a8d382434c5f60990730c4719fc814e85a898a33a1e96c1df8d627d3c6eea6', 'com.android.tools.build:gradle-api:3.4.0:gradle-api-3.4.0.jar:215eca38f6719213c2f492b4d622cdd11676c66c9871f8a2aed0c66d00175628',
'com.android.tools.build:gradle-api:3.5.1:gradle-api-3.5.1.jar:be9b41859bace11998f66b04ed944f87e413f3ad6da3c4665587699da125addc', 'com.android.tools.build:manifest-merger:26.4.0:manifest-merger-26.4.0.jar:29e45e690dedd165035e97c21c2ca94d0bd4ec16b6b210daa26669a582b6f220',
'com.android.tools.build:manifest-merger:26.5.1:manifest-merger-26.5.1.jar:dcad9ecb967251f4d750f55a4204a2b400e8fbfe5cb930a1d0d5dbe10ae8bdfc', 'com.android.tools.ddms:ddmlib:26.4.0:ddmlib-26.4.0.jar:93f56fe4630c3166adbd6c51d7bb602d96abb91b07ba5b1165fdcd071e88c940',
'com.android.tools.ddms:ddmlib:26.5.1:ddmlib-26.5.1.jar:b081aef2a4ed3f4d47cae4cdb128469735f25a114e026d37123bf9ffdec742a8', 'com.android.tools.external.com-intellij:intellij-core:26.4.0:intellij-core-26.4.0.jar:30cb0e879d4424de9677a50b537fb628636b4a50f5470af5e52437980c41421f',
'com.android.tools.external.com-intellij:intellij-core:26.5.1:intellij-core-26.5.1.jar:20eced30adc124805bd93488d9cd9d3e33e6bf7b48e9fe5a703d4983f894d450', 'com.android.tools.external.com-intellij:kotlin-compiler:26.4.0:kotlin-compiler-26.4.0.jar:dd1fe225c31a0e012dc025336363a5b783e2c5c20ffb69e77f8f57e89420d998',
'com.android.tools.external.com-intellij:kotlin-compiler:26.5.1:kotlin-compiler-26.5.1.jar:5aed762dd54875b77ae7018d97c05756ff0c5b9fd02ec595dd396ccd14cc22cb', 'com.android.tools.external.org-jetbrains:uast:26.4.0:uast-26.4.0.jar:f25f3285b775a983327583ff6584dea54e447813ef69e0ce08b05a45b5f4aab0',
'com.android.tools.external.org-jetbrains:uast:26.5.1:uast-26.5.1.jar:4bc8653d6c0943f40fee963a149e36c6baa45683d2530968a13f5007e3c40740', 'com.android.tools.layoutlib:layoutlib-api:26.4.0:layoutlib-api-26.4.0.jar:52128f5cf293b224072be361919bfd416e59480ab7264ddcdbbf046b0d7a12e3',
'com.android.tools.layoutlib:layoutlib-api:26.5.1:layoutlib-api-26.5.1.jar:88732f11396c427273e515d23042e35633f4fe4295528a99b866aa2adf0efd9c', 'com.android.tools.lint:lint-api:26.4.0:lint-api-26.4.0.jar:fdb8fca8ae4c254f438338d03d72605e00ed106f2d5550405af41ca1c8509401',
'com.android.tools.lint:lint-api:26.5.1:lint-api-26.5.1.jar:ec33fcd72bfaf70dd841e03fbfd93f109c2e575aec146067c606689c3972f0de', 'com.android.tools.lint:lint-checks:26.4.0:lint-checks-26.4.0.jar:4ff52d40488cd3e22b9c6b2eb67784e0c3269d0b42ef9d17689cd75a7b2bceb4',
'com.android.tools.lint:lint-checks:26.5.1:lint-checks-26.5.1.jar:a1b9607d484aaae7a71dcecdc76f8003d8239af226c776894a2cf63f9e6c60d7', 'com.android.tools.lint:lint-gradle-api:26.4.0:lint-gradle-api-26.4.0.jar:714b7a85c7d2aa10daeab16e969fe7530c659d0728a7f24021da456870418d0f',
'com.android.tools.lint:lint-gradle-api:26.5.1:lint-gradle-api-26.5.1.jar:82453fd98a8394cc84ed995c04d2cd744abd1d6589403427ba7eef53115406f3', 'com.android.tools.lint:lint-gradle:26.4.0:lint-gradle-26.4.0.jar:b8c130d273f522388734457e1b96790f41528fcec6fda9e8eaa4e4d95a07cfbb',
'com.android.tools.lint:lint-gradle:26.5.1:lint-gradle-26.5.1.jar:59465b56cf7db77c656d5f8195d721c3d48b6bdd0502d774de335bfe4baff00b', 'com.android.tools.lint:lint:26.4.0:lint-26.4.0.jar:83aa062fb0405b60ed358d858c8c2955e1bae44a455b498068c6a60988755f00',
'com.android.tools.lint:lint:26.5.1:lint-26.5.1.jar:336e4b04ec6f8b0f25879131b7a7862d77df83a1879ee5b71be26128755f8e2e', 'com.android.tools:annotations:26.4.0:annotations-26.4.0.jar:a7955b8e19c3a2a861d6faa43a58b7c0d46ea9112188ee3e235c6f9f439ecc1a',
'com.android.tools:annotations:26.5.1:annotations-26.5.1.jar:2c43c82f8c59d8f7a61e3239e1a2dc9f69dc342ec09af9b7c9f69b25337c0b6e', 'com.android.tools:common:26.4.0:common-26.4.0.jar:ea40b94b3c1284ea7700f011388e2906a8363a66abd902891722b3c557984852',
'com.android.tools:common:26.5.1:common-26.5.1.jar:eccfa54486ed54c4e3123cc42195d023bd0dd21bcd2f0e4868e8c6fc70f8ef6b', 'com.android.tools:dvlib:26.4.0:dvlib-26.4.0.jar:23af89c535b01ba36ceed1b6b309b672814eba624e643cd7dedf0519edad50cc',
'com.android.tools:dvlib:26.5.1:dvlib-26.5.1.jar:46f93ad498b4756e7d867d2fe38c38890a80e7407a4ae459e4a8c8d5c5aeacfe', 'com.android.tools:repository:26.4.0:repository-26.4.0.jar:3d1763ab46199374dc6d94129bba11c70f1d5857e2c81a3ac4898abca40b176b',
'com.android.tools:repository:26.5.1:repository-26.5.1.jar:2b3ee791aa4c3e8ce60498c161a27ca7228816fc630eed4d9f25f2f36a106dce', 'com.android.tools:sdk-common:26.4.0:sdk-common-26.4.0.jar:78a522525b30ffc6b7bf1299c831d24ce385f68a9f4878f8f752e9baefa31b0f',
'com.android.tools:sdk-common:26.5.1:sdk-common-26.5.1.jar:365f749676c3574676fd465177c8a492f340816db2b520d6ed114d3b6e77bea7', 'com.android.tools:sdklib:26.4.0:sdklib-26.4.0.jar:b854c23892013a326d761cf071c72cf3e038ed0469d10f4a356829fa56e4c132',
'com.android.tools:sdklib:26.5.1:sdklib-26.5.1.jar:007da104afb27c8c682a1628023fe9ec438249c8d15ef0fd6624c5bb8e23b696', 'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.code.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81', 'com.google.code.gson:gson:2.8.0:gson-2.8.0.jar:c6221763bd79c4f1c3dc7f750b5f29a0bb38b367b81314c4f71896e340c40825',
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f', 'com.google.dagger:dagger-compiler:2.22.1:dagger-compiler-2.22.1.jar:e5f28302cbe70a79d3620cddebfb8ec0736814f3980ffe1e673bfe3342f507d3',
'com.google.dagger:dagger-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a', 'com.google.dagger:dagger-producers:2.22.1:dagger-producers-2.22.1.jar:f834a0082014213a68ff06a0f048d750178d02196c58b0b15beb367d32b97e35',
'com.google.dagger:dagger-spi:2.24:dagger-spi-2.24.jar:c038445d14dbcb4054e61bf49e05009edf26fce4fdc7ec1a9db544784f68e718', 'com.google.dagger:dagger-spi:2.22.1:dagger-spi-2.22.1.jar:4b0b922793b3bcb91b99fabb75dba77c68afd7ae4c5f0c4fd6ba681f0a291c7d',
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64', 'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a',
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a', 'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30', 'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e', 'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26', 'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9',
'com.google.guava:guava:27.0.1-jre:guava-27.0.1-jre.jar:e1c814fd04492a27c38e0317eabeaa1b3e950ec8010239e400fe90ad6c9107b4', 'com.google.guava:guava:26.0-jre:guava-26.0-jre.jar:a0e9cabad665bc20bcd2b01f108e5fc03f756e13aea80abaadb9f407033bea2c',
'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6', 'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd', 'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4', 'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
@@ -57,7 +55,6 @@ dependencyVerification {
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2', 'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5', 'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2', 'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
@@ -69,22 +66,22 @@ dependencyVerification {
'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.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.9:obfs4proxy-android-0.0.9.zip:9b7e9181535ea8d8bbe8ae6338e08cf4c5fc1e357a779393e0ce49586d459ae0',
'org.briarproject:tor-android:0.3.5.9:tor-android-0.3.5.9.zip:853b0440feccd6904bd03e6b2de53a62ebcde1d58068beeadc447a7dff950bc8', 'org.briarproject:tor-android:0.3.5.8:tor-android-0.3.5.8.zip:42a13a6f185be1a62f42e3f30ce66a3c099ac5ec890a65e7593111b65b44a54a',
'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',
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53', 'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa', 'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70', 'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea', 'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jetbrains.kotlin:kotlin-reflect:1.3.50:kotlin-reflect-1.3.50.jar:64583199ea5a54aefd1bd1595288925f784226ee562d1dd279011c6075b3d7a4', 'org.jetbrains.kotlin:kotlin-reflect:1.3.21:kotlin-reflect-1.3.21.jar:a3065c822633191e0a3e3ee12a29bec234fc4b2864a6bb87ef48cce3e9e0c26a',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50:kotlin-stdlib-common-1.3.50.jar:8ce678e88e4ba018b66dacecf952471e4d7dfee156a8a819760a5a5ff29d323c', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.21:kotlin-stdlib-common-1.3.21.jar:cea61f7b611895e64f58569a9757fc0ab0d582f107211e1930e0ce2a0add52a7',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50:kotlin-stdlib-jdk7-1.3.50.jar:9a026639e76212f8d57b86d55b075394c2e009f1979110751d34c05c5f75d57b', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21:kotlin-stdlib-jdk7-1.3.21.jar:a87875604fd42140da6938ae4d35ee61081f4482536efc6d2615b8b626a198af',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50:kotlin-stdlib-jdk8-1.3.50.jar:1b351fb6e09c14b55525c74c1f4cf48942eae43c348b7bc764a5e6e423d4da0c', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21:kotlin-stdlib-jdk8-1.3.21.jar:5823ed66ac122a1c55442ebca5a209a843ccd87f562edc31a787f3d2e47f74d4',
'org.jetbrains.kotlin:kotlin-stdlib:1.3.50:kotlin-stdlib-1.3.50.jar:e6f05746ee0366d0b52825a090fac474dcf44082c9083bbb205bd16976488d6c', 'org.jetbrains.kotlin:kotlin-stdlib:1.3.21:kotlin-stdlib-1.3.21.jar:38ba2370d9f06f50433e06b2ca775b94473c2e2785f410926079ab793c72b034',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7', 'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478', 'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',

View File

@@ -7,7 +7,7 @@ apply plugin: 'witness'
apply from: 'witness.gradle' apply from: 'witness.gradle'
dependencies { dependencies {
implementation "com.google.dagger:dagger:2.24" implementation "com.google.dagger:dagger:2.22.1"
implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation 'com.google.code.findbugs:jsr305:3.0.2'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api;
/**
* Interface for specifying which features are enabled in a build.
*/
public interface FeatureFlags {
boolean shouldEnableImageAttachments();
}

View File

@@ -25,10 +25,7 @@ public interface ClientHelper {
throws DbException, FormatException; throws DbException, FormatException;
void addLocalMessage(Transaction txn, Message m, BdfDictionary metadata, void addLocalMessage(Transaction txn, Message m, BdfDictionary metadata,
boolean shared, boolean temporary) boolean shared) throws DbException, FormatException;
throws DbException, FormatException;
Message createMessage(GroupId g, long timestamp, byte[] body);
Message createMessage(GroupId g, long timestamp, BdfList body) Message createMessage(GroupId g, long timestamp, BdfList body)
throws FormatException; throws FormatException;
@@ -111,7 +108,7 @@ public interface ClientHelper {
Author parseAndValidateAuthor(BdfList author) throws FormatException; Author parseAndValidateAuthor(BdfList author) throws FormatException;
PublicKey parseAndValidateAgreementPublicKey(byte[] publicKeyBytes) PublicKey parseAndValidateAgreementPublicKey(byte[] publicKeyBytes)
throws FormatException; throws FormatException;
TransportProperties parseAndValidateTransportProperties( TransportProperties parseAndValidateTransportProperties(
BdfDictionary properties) throws FormatException; BdfDictionary properties) throws FormatException;

View File

@@ -4,10 +4,8 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.UnsupportedVersionException; import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException; import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.PendingContactExistsException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -119,14 +117,9 @@ public interface ContactManager {
* @throws FormatException If the link is invalid * @throws FormatException If the link is invalid
* @throws GeneralSecurityException If the pending contact's handshake * @throws GeneralSecurityException If the pending contact's handshake
* public key is invalid * public key is invalid
* @throws ContactExistsException If a contact with the same handshake
* public key already exists
* @throws PendingContactExistsException If a pending contact with the same
* handshake public key already exists
*/ */
PendingContact addPendingContact(String link, String alias) PendingContact addPendingContact(String link, String alias)
throws DbException, FormatException, GeneralSecurityException, throws DbException, FormatException, GeneralSecurityException;
ContactExistsException, PendingContactExistsException;
/** /**
* Returns the pending contact with the given ID. * Returns the pending contact with the given ID.

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.contact;
public enum PendingContactState { public enum PendingContactState {
WAITING_FOR_CONNECTION, WAITING_FOR_CONNECTION,
OFFLINE,
CONNECTING, CONNECTING,
ADDING_CONTACT, ADDING_CONTACT,
FAILED FAILED

View File

@@ -132,32 +132,17 @@ public interface CryptoComponent {
* storage. The encryption and authentication keys are derived from the * storage. The encryption and authentication keys are derived from the
* given password. The ciphertext will be decryptable using the same * given password. The ciphertext will be decryptable using the same
* password after the app restarts. * password after the app restarts.
*
* @param keyStrengthener Used to strengthen the password-based key. If
* null, the password-based key will not be strengthened
*/ */
byte[] encryptWithPassword(byte[] plaintext, String password, byte[] encryptWithPassword(byte[] plaintext, String password);
@Nullable KeyStrengthener keyStrengthener);
/** /**
* 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. Returns null if the ciphertext cannot be decrypted and * given password. Returns null if the ciphertext cannot be decrypted and
* authenticated (for example, if the password is wrong). * authenticated (for example, if the password is wrong).
*
* @param keyStrengthener Used to strengthen the password-based key. If
* null, or if strengthening was not used when encrypting the ciphertext,
* the password-based key will not be strengthened
*/ */
@Nullable @Nullable
byte[] decryptWithPassword(byte[] ciphertext, String password, byte[] decryptWithPassword(byte[] ciphertext, String password);
@Nullable KeyStrengthener keyStrengthener);
/**
* Returns true if the given ciphertext was encrypted using a strengthened
* key. The validity of the ciphertext is not checked.
*/
boolean isEncryptedWithStrengthenedKey(byte[] ciphertext);
/** /**
* Encrypts the given plaintext to the given public key. * Encrypts the given plaintext to the given public key.

View File

@@ -1,23 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* Interface for strengthening a password-based key, for example by using a
* key stored in a key management service or hardware security module.
*/
@NotNullByDefault
public interface KeyStrengthener {
/**
* Returns true if the strengthener has been initialised.
*/
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
boolean isInitialised();
/**
* Initialises the strengthener if necessary and returns a strong key
* derived from the given key.
*/
SecretKey strengthenKey(SecretKey k);
}

View File

@@ -29,7 +29,6 @@ import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -78,12 +77,12 @@ public interface DatabaseComponent extends TransactionManager {
* Stores a local message. * Stores a local message.
*/ */
void addLocalMessage(Transaction txn, Message m, Metadata meta, void addLocalMessage(Transaction txn, Message m, Metadata meta,
boolean shared, boolean temporary) throws DbException; boolean shared) throws DbException;
/** /**
* Stores a pending contact. * Stores a pending contact.
*/ */
void addPendingContact(Transaction txn, PendingContact p, AuthorId local) void addPendingContact(Transaction txn, PendingContact p)
throws DbException; throws DbException;
/** /**
@@ -428,13 +427,6 @@ public interface DatabaseComponent extends TransactionManager {
*/ */
Settings getSettings(Transaction txn, String namespace) throws DbException; Settings getSettings(Transaction txn, String namespace) throws DbException;
/**
* Returns the versions of the sync protocol supported by the given contact.
* <p/>
* Read-only.
*/
List<Byte> getSyncVersions(Transaction txn, ContactId c) throws DbException;
/** /**
* Returns all transport keys for the given transport. * Returns all transport keys for the given transport.
* <p/> * <p/>
@@ -518,12 +510,6 @@ public interface DatabaseComponent extends TransactionManager {
void removePendingContact(Transaction txn, PendingContactId p) void removePendingContact(Transaction txn, PendingContactId p)
throws DbException; throws DbException;
/**
* Removes all temporary messages (and all associated state) from the
* database.
*/
void removeTemporaryMessages(Transaction txn) throws DbException;
/** /**
* Removes a transport (and all associated state) from the database. * Removes a transport (and all associated state) from the database.
*/ */
@@ -552,11 +538,6 @@ public interface DatabaseComponent extends TransactionManager {
void setGroupVisibility(Transaction txn, ContactId c, GroupId g, void setGroupVisibility(Transaction txn, ContactId c, GroupId g,
Visibility v) throws DbException; Visibility v) throws DbException;
/**
* Marks the given message as permanent, i.e. not temporary.
*/
void setMessagePermanent(Transaction txn, MessageId m) throws DbException;
/** /**
* Marks the given message as shared. * Marks the given message as shared.
*/ */
@@ -587,12 +568,6 @@ public interface DatabaseComponent extends TransactionManager {
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t, void setReorderingWindow(Transaction txn, KeySetId k, TransportId t,
long timePeriod, long base, byte[] bitmap) throws DbException; long timePeriod, long base, byte[] bitmap) throws DbException;
/**
* Sets the versions of the sync protocol supported by the given contact.
*/
void setSyncVersions(Transaction txn, ContactId c, List<Byte> supported)
throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */

View File

@@ -1,29 +1,13 @@
package org.briarproject.bramble.api.db; package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File; import java.io.File;
import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
public interface DatabaseConfig { public interface DatabaseConfig {
/**
* Returns the directory where the database stores its data.
*/
File getDatabaseDirectory(); File getDatabaseDirectory();
/**
* Returns the directory where the encrypted database key is stored.
*/
File getDatabaseKeyDirectory(); File getDatabaseKeyDirectory();
/**
* Returns a {@link KeyStrengthener} for strengthening the encryption of
* the database key, or null if no strengthener should be used.
*/
@Nullable
KeyStrengthener getKeyStrengthener();
} }

View File

@@ -1,21 +1,9 @@
package org.briarproject.bramble.api.db; package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.contact.PendingContact;
/** /**
* Thrown when a duplicate pending contact is added to the database. This * Thrown when a duplicate pending contact is added to the database. This
* exception may occur due to concurrent updates and does not indicate a * exception may occur due to concurrent updates and does not indicate a
* database error. * database error.
*/ */
public class PendingContactExistsException extends DbException { public class PendingContactExistsException extends DbException {
private final PendingContact pendingContact;
public PendingContactExistsException(PendingContact pendingContact) {
this.pendingContact = pendingContact;
}
public PendingContact getPendingContact() {
return pendingContact;
}
} }

View File

@@ -18,8 +18,6 @@ public interface EventBus {
/** /**
* Asynchronously notifies all listeners of an event. Listeners are * Asynchronously notifies all listeners of an event. Listeners are
* notified on the {@link EventExecutor}. * notified on the {@link EventExecutor}.
* <p>
* This method can safely be called while holding a lock.
*/ */
void broadcast(Event e); void broadcast(Event e);
} }

View File

@@ -8,4 +8,6 @@ public interface BluetoothConstants {
String PROP_ADDRESS = "address"; String PROP_ADDRESS = "address";
String PROP_UUID = "uuid"; String PROP_UUID = "uuid";
String PREF_BT_ENABLE = "enable";
} }

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.properties.TransportProperties;
/**
* An interface for handling peers discovered by transport plugins.
*/
@NotNullByDefault
public interface DiscoveryHandler {
/**
* Handles a peer discovered by a transport plugin.
*
* @param p A set of properties describing the discovered peer.
*/
void handleDevice(TransportProperties p);
}

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

@@ -3,55 +3,12 @@ package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.SettingsManager;
import java.util.List; import java.util.Collection;
@NotNullByDefault @NotNullByDefault
public interface Plugin { public interface Plugin {
enum State {
/**
* The plugin has not finished starting or has been stopped.
*/
STARTING_STOPPING,
/**
* The plugin is disabled by settings. Use {@link #getReasonsDisabled()}
* to find out which settings are responsible.
*/
DISABLED,
/**
* The plugin is being enabled and can't yet make or receive
* connections.
*/
ENABLING,
/**
* The plugin is enabled and can make or receive connections.
*/
ACTIVE,
/**
* The plugin is enabled but can't make or receive connections
*/
INACTIVE
}
/**
* The string for the boolean preference
* to use with the {@link SettingsManager} to enable or disable the plugin.
*/
String PREF_PLUGIN_ENABLE = "enable";
/**
* Reason flag returned by {@link #getReasonsDisabled()} to indicate that
* the plugin has been disabled by the user.
*/
int REASON_USER = 1;
/** /**
* Returns the plugin's transport identifier. * Returns the plugin's transport identifier.
*/ */
@@ -78,18 +35,9 @@ public interface Plugin {
void stop() throws PluginException; void stop() throws PluginException;
/** /**
* Returns the current state of the plugin. * Returns true if the plugin is running.
*/ */
State getState(); boolean isRunning();
/**
* Returns a set of flags indicating why the plugin is
* {@link State#DISABLED disabled}, or 0 if the plugin is not disabled.
* <p>
* The flags used are plugin-specific, except the generic flag
* {@link #REASON_USER}, which may be used by any plugin.
*/
int getReasonsDisabled();
/** /**
* Returns true if the plugin should be polled periodically to attempt to * Returns true if the plugin should be polled periodically to attempt to
@@ -106,5 +54,6 @@ public interface Plugin {
* Attempts to create connections using the given transport properties, * Attempts to create connections using the given transport properties,
* passing any created connections to the corresponding handlers. * passing any created connections to the corresponding handlers.
*/ */
void poll(List<Pair<TransportProperties, ConnectionHandler>> properties); void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
properties);
} }

View File

@@ -1,10 +1,6 @@
package org.briarproject.bramble.api.plugin; package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin.State;
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
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;
@@ -36,17 +32,12 @@ public interface PluginCallback extends ConnectionHandler {
void mergeLocalProperties(TransportProperties p); void mergeLocalProperties(TransportProperties p);
/** /**
* Informs the callback of the plugin's current state. * Signals that the transport is enabled.
* <p>
* If the current state is different from the previous state, the callback
* will broadcast a {@link TransportStateEvent}. If the current state is
* {@link State#ACTIVE} and the previous state was not
* {@link State#ACTIVE}, the callback will broadcast a
* {@link TransportActiveEvent}. If the current state is not
* {@link State#ACTIVE} and the previous state was {@link State#ACTIVE},
* the callback will broadcast a {@link TransportInactiveEvent}.
* <p>
* This method can safely be called while holding a lock.
*/ */
void pluginStateChanged(State state); void transportEnabled();
/**
* Signals that the transport is disabled.
*/
void transportDisabled();
} }

View File

@@ -41,17 +41,4 @@ public interface PluginManager {
* Returns any duplex plugins that support rendezvous. * Returns any duplex plugins that support rendezvous.
*/ */
Collection<DuplexPlugin> getRendezvousPlugins(); Collection<DuplexPlugin> getRendezvousPlugins();
/**
* Enables or disables the plugin
* identified by the given {@link TransportId}.
* <p>
* Note that this applies the change asynchronously
* and there are no order guarantees.
* <p>
* If no plugin with the given {@link TransportId} is registered,
* this is a no-op.
*/
void setPluginEnabled(TransportId t, boolean enabled);
} }

View File

@@ -21,21 +21,6 @@ public interface TorConstants {
int PREF_TOR_NETWORK_AUTOMATIC = 0; int PREF_TOR_NETWORK_AUTOMATIC = 0;
int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1; int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1;
int PREF_TOR_NETWORK_WITH_BRIDGES = 2; int PREF_TOR_NETWORK_WITH_BRIDGES = 2;
// TODO: Remove when settings migration code is removed
int PREF_TOR_NETWORK_NEVER = 3; int PREF_TOR_NETWORK_NEVER = 3;
/**
* Reason flag returned by {@link Plugin#getReasonsDisabled()}.
*/
int REASON_BATTERY = 2;
/**
* Reason flag returned by {@link Plugin#getReasonsDisabled()}.
*/
int REASON_MOBILE_DATA = 4;
/**
* Reason flag returned by {@link Plugin#getReasonsDisabled()}.
*/
int REASON_COUNTRY_BLOCKED = 8;
} }

View File

@@ -1,18 +1,14 @@
package org.briarproject.bramble.api.plugin.duplex; package org.briarproject.bramble.api.plugin.duplex;
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.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
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.DiscoveryHandler;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
@@ -62,16 +58,4 @@ public interface DuplexPlugin extends Plugin {
@Nullable @Nullable
RendezvousEndpoint createRendezvousEndpoint(KeyMaterialSource k, RendezvousEndpoint createRendezvousEndpoint(KeyMaterialSource k,
boolean alice, ConnectionHandler incoming); boolean alice, ConnectionHandler incoming);
/**
* Returns true if the plugin supports peer discovery.
*/
boolean supportsDiscovery();
/**
* Attempts to discover peers using the given transport properties, passing
* any discovered peers to the corresponding handlers.
*/
void discoverPeers(List<Pair<TransportProperties, DiscoveryHandler>>
properties);
} }

View File

@@ -2,22 +2,20 @@ package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin.State;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* An event that is broadcast when a plugin enters the {@link State#ACTIVE} * An event that is broadcast when a transport is disabled.
* state.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class TransportActiveEvent extends Event { public class TransportDisabledEvent extends Event {
private final TransportId transportId; private final TransportId transportId;
public TransportActiveEvent(TransportId transportId) { public TransportDisabledEvent(TransportId transportId) {
this.transportId = transportId; this.transportId = transportId;
} }

View File

@@ -2,22 +2,20 @@ package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin.State;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* An event that is broadcast when a plugin leaves the {@link State#ACTIVE} * An event that is broadcast when a transport is enabled.
* state.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class TransportInactiveEvent extends Event { public class TransportEnabledEvent extends Event {
private final TransportId transportId; private final TransportId transportId;
public TransportInactiveEvent(TransportId transportId) { public TransportEnabledEvent(TransportId transportId) {
this.transportId = transportId; this.transportId = transportId;
} }

View File

@@ -1,32 +0,0 @@
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin.State;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when the {@link State state} of a plugin changes.
*/
@Immutable
@NotNullByDefault
public class TransportStateEvent extends Event {
private final TransportId transportId;
private final State state;
public TransportStateEvent(TransportId transportId, State state) {
this.transportId = transportId;
this.state = state;
}
public TransportId getTransportId() {
return transportId;
}
public State getState() {
return state;
}
}

View File

@@ -1,16 +1,10 @@
package org.briarproject.bramble.api.sync; package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
import javax.annotation.concurrent.Immutable;
/** /**
* A record acknowledging receipt of one or more {@link Message Messages}. * A record acknowledging receipt of one or more {@link Message Messages}.
*/ */
@Immutable
@NotNullByDefault
public class Ack { public class Ack {
private final Collection<MessageId> acked; private final Collection<MessageId> acked;

View File

@@ -1,14 +1,8 @@
package org.briarproject.bramble.api.sync; package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
@Immutable
@NotNullByDefault
public class Message { public class Message {
/** /**

View File

@@ -1,16 +1,10 @@
package org.briarproject.bramble.api.sync; package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
import javax.annotation.concurrent.Immutable;
/** /**
* A record offering the recipient one or more {@link Message Messages}. * A record offering the recipient one or more {@link Message Messages}.
*/ */
@Immutable
@NotNullByDefault
public class Offer { public class Offer {
private final Collection<MessageId> offered; private final Collection<MessageId> offered;

View File

@@ -9,5 +9,5 @@ public interface RecordTypes {
byte MESSAGE = 1; byte MESSAGE = 1;
byte OFFER = 2; byte OFFER = 2;
byte REQUEST = 3; byte REQUEST = 3;
byte VERSIONS = 4;
} }

View File

@@ -1,16 +1,10 @@
package org.briarproject.bramble.api.sync; package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
import javax.annotation.concurrent.Immutable;
/** /**
* A record requesting one or more {@link Message Messages} from the recipient. * A record requesting one or more {@link Message Messages} from the recipient.
*/ */
@Immutable
@NotNullByDefault
public class Request { public class Request {
private final Collection<MessageId> requested; private final Collection<MessageId> requested;

View File

@@ -2,9 +2,6 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.UniqueId; import org.briarproject.bramble.api.UniqueId;
import java.util.List;
import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES; import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
public interface SyncConstants { public interface SyncConstants {
@@ -14,11 +11,6 @@ public interface SyncConstants {
*/ */
byte PROTOCOL_VERSION = 0; byte PROTOCOL_VERSION = 0;
/**
* The versions of the sync protocol this peer supports.
*/
List<Byte> SUPPORTED_VERSIONS = singletonList(PROTOCOL_VERSION);
/** /**
* The maximum length of a group descriptor in bytes. * The maximum length of a group descriptor in bytes.
*/ */
@@ -43,10 +35,4 @@ public interface SyncConstants {
* The maximum number of message IDs in an ack, offer or request record. * The maximum number of message IDs in an ack, offer or request record.
*/ */
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH; int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH;
/**
* The maximum number of versions of the sync protocol a peer may support
* simultaneously.
*/
int MAX_SUPPORTED_VERSIONS = 10;
} }

View File

@@ -25,7 +25,4 @@ public interface SyncRecordReader {
Request readRequest() throws IOException; Request readRequest() throws IOException;
boolean hasVersions() throws IOException;
Versions readVersions() throws IOException;
} }

View File

@@ -15,7 +15,5 @@ public interface SyncRecordWriter {
void writeRequest(Request r) throws IOException; void writeRequest(Request r) throws IOException;
void writeVersions(Versions v) throws IOException;
void flush() throws IOException; void flush() throws IOException;
} }

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.List;
import javax.annotation.concurrent.Immutable;
/**
* A record telling the recipient which versions of the sync protocol the
* sender supports.
*/
@Immutable
@NotNullByDefault
public class Versions {
private final List<Byte> supported;
public Versions(List<Byte> supported) {
this.supported = supported;
}
public List<Byte> getSupportedVersions() {
return supported;
}
}

View File

@@ -1,34 +0,0 @@
package org.briarproject.bramble.api.sync.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.List;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when the versions of the sync protocol supported
* by a contact are updated.
*/
@Immutable
@NotNullByDefault
public class SyncVersionsUpdatedEvent extends Event {
private final ContactId contactId;
private final List<Byte> supported;
public SyncVersionsUpdatedEvent(ContactId contactId, List<Byte> supported) {
this.contactId = contactId;
this.supported = supported;
}
public ContactId getContactId() {
return contactId;
}
public List<Byte> getSupportedVersions() {
return supported;
}
}

View File

@@ -1,63 +0,0 @@
package org.briarproject.bramble.api.versioning;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ClientVersion implements Comparable<ClientVersion> {
private final ClientMajorVersion majorVersion;
private final int minorVersion;
public ClientVersion(ClientMajorVersion majorVersion,
int minorVersion) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
}
public ClientVersion(ClientId clientId, int majorVersion,
int minorVersion) {
this(new ClientMajorVersion(clientId, majorVersion), minorVersion);
}
public ClientMajorVersion getClientMajorVersion() {
return majorVersion;
}
public ClientId getClientId() {
return majorVersion.getClientId();
}
public int getMajorVersion() {
return majorVersion.getMajorVersion();
}
public int getMinorVersion() {
return minorVersion;
}
@Override
public boolean equals(Object o) {
if (o instanceof ClientVersion) {
ClientVersion cv = (ClientVersion) o;
return majorVersion.equals(cv.majorVersion)
&& minorVersion == cv.minorVersion;
}
return false;
}
@Override
public int hashCode() {
return majorVersion.hashCode();
}
@Override
public int compareTo(ClientVersion cv) {
int compare = majorVersion.compareTo(cv.majorVersion);
if (compare != 0) return compare;
return minorVersion - cv.minorVersion;
}
}

View File

@@ -1,34 +0,0 @@
package org.briarproject.bramble.api.versioning.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.versioning.ClientVersion;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when we receive a client versioning update from
* a contact.
*/
@Immutable
@NotNullByDefault
public class ClientVersionUpdatedEvent extends Event {
private final ContactId contactId;
private final ClientVersion clientVersion;
public ClientVersionUpdatedEvent(ContactId contactId,
ClientVersion clientVersion) {
this.contactId = contactId;
this.clientVersion = clientVersion;
}
public ContactId getContactId() {
return contactId;
}
public ClientVersion getClientVersion() {
return clientVersion;
}
}

View File

@@ -2,7 +2,7 @@ dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64', 'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',

View File

@@ -17,7 +17,7 @@ dependencies {
implementation 'org.whispersystems:curve25519-java:0.5.0' implementation 'org.whispersystems:curve25519-java:0.5.0'
implementation 'org.briarproject:jtorctl:0.3' implementation 'org.briarproject:jtorctl:0.3'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24' annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6 testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
@@ -26,7 +26,7 @@ dependencies {
testImplementation "org.jmock:jmock-junit4:2.8.2" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:2.8.2" testImplementation "org.jmock:jmock-legacy:2.8.2"
testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.24' testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
signature 'org.codehaus.mojo.signature:java16:1.1@signature' signature 'org.codehaus.mojo.signature:java16:1.1@signature'
} }

View File

@@ -39,21 +39,18 @@ public interface BrambleCoreEagerSingletons {
void inject(VersioningModule.EagerSingletons init); void inject(VersioningModule.EagerSingletons init);
class Helper { default void injectBrambleCoreEagerSingletons() {
inject(new ContactModule.EagerSingletons());
public static void injectEagerSingletons(BrambleCoreEagerSingletons c) { inject(new CryptoExecutorModule.EagerSingletons());
c.inject(new ContactModule.EagerSingletons()); inject(new DatabaseExecutorModule.EagerSingletons());
c.inject(new CryptoExecutorModule.EagerSingletons()); inject(new IdentityModule.EagerSingletons());
c.inject(new DatabaseExecutorModule.EagerSingletons()); inject(new LifecycleModule.EagerSingletons());
c.inject(new IdentityModule.EagerSingletons()); inject(new RendezvousModule.EagerSingletons());
c.inject(new LifecycleModule.EagerSingletons()); inject(new PluginModule.EagerSingletons());
c.inject(new RendezvousModule.EagerSingletons()); inject(new PropertiesModule.EagerSingletons());
c.inject(new PluginModule.EagerSingletons()); inject(new SystemModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons()); inject(new TransportModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons()); inject(new ValidationModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons()); inject(new VersioningModule.EagerSingletons());
c.inject(new ValidationModule.EagerSingletons());
c.inject(new VersioningModule.EagerSingletons());
}
} }
} }

View File

@@ -50,4 +50,8 @@ import dagger.Module;
VersioningModule.class VersioningModule.class
}) })
public class BrambleCoreModule { public class BrambleCoreModule {
public static void initEagerSingletons(BrambleCoreEagerSingletons c) {
c.injectBrambleCoreEagerSingletons();
}
} }

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.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;
import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.identity.Identity;
@@ -20,7 +19,6 @@ import java.io.InputStreamReader;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
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;
@@ -70,10 +68,9 @@ class AccountManagerImpl implements AccountManager {
return databaseKey; return databaseKey;
} }
// Package access for testing // Locking: stateChangeLock
@GuardedBy("stateChangeLock")
@Nullable @Nullable
String loadEncryptedDatabaseKey() { protected String loadEncryptedDatabaseKey() {
String key = readDbKeyFromFile(dbKeyFile); String key = readDbKeyFromFile(dbKeyFile);
if (key == null) { if (key == null) {
LOG.info("No database key in primary file"); LOG.info("No database key in primary file");
@@ -86,7 +83,7 @@ class AccountManagerImpl implements AccountManager {
return key; return key;
} }
@GuardedBy("stateChangeLock") // Locking: stateChangeLock
@Nullable @Nullable
private String readDbKeyFromFile(File f) { private String readDbKeyFromFile(File f) {
if (!f.exists()) { if (!f.exists()) {
@@ -105,9 +102,8 @@ class AccountManagerImpl implements AccountManager {
} }
} }
// Package access for testing // Locking: stateChangeLock
@GuardedBy("stateChangeLock") protected boolean storeEncryptedDatabaseKey(String hex) {
boolean storeEncryptedDatabaseKey(String hex) {
LOG.info("Storing database key in file"); LOG.info("Storing database key in file");
// Create the directory if necessary // Create the directory if necessary
if (databaseConfig.getDatabaseKeyDirectory().mkdirs()) if (databaseConfig.getDatabaseKeyDirectory().mkdirs())
@@ -144,7 +140,7 @@ class AccountManagerImpl implements AccountManager {
} }
} }
@GuardedBy("stateChangeLock") // Locking: 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("UTF-8")); out.write(key.getBytes("UTF-8"));
@@ -174,11 +170,10 @@ class AccountManagerImpl implements AccountManager {
} }
} }
@GuardedBy("stateChangeLock") // Locking: stateChangeLock
private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) { private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) {
byte[] plaintext = key.getBytes(); byte[] plaintext = key.getBytes();
byte[] ciphertext = crypto.encryptWithPassword(plaintext, password, byte[] ciphertext = crypto.encryptWithPassword(plaintext, password);
databaseConfig.getKeyStrengthener());
return storeEncryptedDatabaseKey(toHexString(ciphertext)); return storeEncryptedDatabaseKey(toHexString(ciphertext));
} }
@@ -202,7 +197,7 @@ class AccountManagerImpl implements AccountManager {
} }
} }
@GuardedBy("stateChangeLock") // Locking: stateChangeLock
@Nullable @Nullable
private SecretKey loadAndDecryptDatabaseKey(String password) { private SecretKey loadAndDecryptDatabaseKey(String password) {
String hex = loadEncryptedDatabaseKey(); String hex = loadEncryptedDatabaseKey();
@@ -211,22 +206,12 @@ class AccountManagerImpl implements AccountManager {
return null; return null;
} }
byte[] ciphertext = fromHexString(hex); byte[] ciphertext = fromHexString(hex);
KeyStrengthener keyStrengthener = databaseConfig.getKeyStrengthener(); byte[] plaintext = crypto.decryptWithPassword(ciphertext, password);
byte[] plaintext = crypto.decryptWithPassword(ciphertext, password,
keyStrengthener);
if (plaintext == null) { if (plaintext == null) {
LOG.info("Failed to decrypt database key"); LOG.info("Failed to decrypt database key");
return null; return null;
} }
SecretKey key = new SecretKey(plaintext); return new SecretKey(plaintext);
// 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
if (keyStrengthener != null &&
!crypto.isEncryptedWithStrengthenedKey(ciphertext)) {
LOG.info("Re-encrypting database key with strengthened key");
encryptAndStoreDatabaseKey(key, password);
}
return key;
} }
@Override @Override

View File

@@ -85,21 +85,14 @@ class ClientHelperImpl implements ClientHelper {
@Override @Override
public void addLocalMessage(Message m, BdfDictionary metadata, public void addLocalMessage(Message m, BdfDictionary metadata,
boolean shared) throws DbException, FormatException { boolean shared) throws DbException, FormatException {
db.transaction(false, txn -> addLocalMessage(txn, m, metadata, shared, db.transaction(false, txn -> addLocalMessage(txn, m, metadata, shared));
false));
} }
@Override @Override
public void addLocalMessage(Transaction txn, Message m, public void addLocalMessage(Transaction txn, Message m,
BdfDictionary metadata, boolean shared, boolean temporary) BdfDictionary metadata, boolean shared)
throws DbException, FormatException { throws DbException, FormatException {
db.addLocalMessage(txn, m, metadataEncoder.encode(metadata), shared, db.addLocalMessage(txn, m, metadataEncoder.encode(metadata), shared);
temporary);
}
@Override
public Message createMessage(GroupId g, long timestamp, byte[] body) {
return messageFactory.createMessage(g, timestamp, body);
} }
@Override @Override

View File

@@ -139,8 +139,7 @@ class ContactManagerImpl implements ContactManager, EventListener {
pendingContactFactory.createPendingContact(link, alias); pendingContactFactory.createPendingContact(link, alias);
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
AuthorId local = identityManager.getLocalAuthor(txn).getId(); db.addPendingContact(txn, p);
db.addPendingContact(txn, p, local);
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn); KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
keyManager.addPendingContact(txn, p.getId(), p.getPublicKey(), keyManager.addPendingContact(txn, p.getId(), p.getPublicKey(),
ourKeyPair); ourKeyPair);
@@ -148,6 +147,7 @@ class ContactManagerImpl implements ContactManager, EventListener {
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);
} }
states.put(p.getId(), WAITING_FOR_CONNECTION);
return p; return p;
} }

View File

@@ -9,7 +9,6 @@ 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.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.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
@@ -52,8 +51,7 @@ class CryptoComponentImpl implements CryptoComponent {
private static final int SIGNATURE_KEY_PAIR_BITS = 256; private static final int SIGNATURE_KEY_PAIR_BITS = 256;
private static final int STORAGE_IV_BYTES = 24; // 196 bits private static final int STORAGE_IV_BYTES = 24; // 196 bits
private static final int PBKDF_SALT_BYTES = 32; // 256 bits private static final int PBKDF_SALT_BYTES = 32; // 256 bits
private static final byte PBKDF_FORMAT_SCRYPT = 0; private static final int PBKDF_FORMAT_SCRYPT = 0;
private static final byte PBKDF_FORMAT_SCRYPT_STRENGTHENED = 1;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final PasswordBasedKdf passwordBasedKdf; private final PasswordBasedKdf passwordBasedKdf;
@@ -313,8 +311,7 @@ class CryptoComponentImpl implements CryptoComponent {
} }
@Override @Override
public byte[] encryptWithPassword(byte[] input, String password, public byte[] encryptWithPassword(byte[] input, String password) {
@Nullable KeyStrengthener keyStrengthener) {
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher(); AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
int macBytes = cipher.getMacBytes(); int macBytes = cipher.getMacBytes();
// Generate a random salt // Generate a random salt
@@ -322,9 +319,8 @@ class CryptoComponentImpl implements CryptoComponent {
secureRandom.nextBytes(salt); secureRandom.nextBytes(salt);
// Calibrate the KDF // Calibrate the KDF
int cost = passwordBasedKdf.chooseCostParameter(); int cost = passwordBasedKdf.chooseCostParameter();
// Derive the encryption key from the password // Derive the key from the password
SecretKey key = passwordBasedKdf.deriveKey(password, salt, cost); SecretKey key = passwordBasedKdf.deriveKey(password, salt, cost);
if (keyStrengthener != null) key = keyStrengthener.strengthenKey(key);
// Generate a random IV // Generate a random IV
byte[] iv = new byte[STORAGE_IV_BYTES]; byte[] iv = new byte[STORAGE_IV_BYTES];
secureRandom.nextBytes(iv); secureRandom.nextBytes(iv);
@@ -335,9 +331,7 @@ class CryptoComponentImpl implements CryptoComponent {
byte[] output = new byte[outputLen]; byte[] output = new byte[outputLen];
int outputOff = 0; int outputOff = 0;
// Format version // Format version
byte formatVersion = keyStrengthener == null output[outputOff] = PBKDF_FORMAT_SCRYPT;
? PBKDF_FORMAT_SCRYPT : PBKDF_FORMAT_SCRYPT_STRENGTHENED;
output[outputOff] = formatVersion;
outputOff++; outputOff++;
// Salt // Salt
arraycopy(salt, 0, output, outputOff, salt.length); arraycopy(salt, 0, output, outputOff, salt.length);
@@ -360,8 +354,7 @@ class CryptoComponentImpl implements CryptoComponent {
@Override @Override
@Nullable @Nullable
public byte[] decryptWithPassword(byte[] input, String password, public byte[] decryptWithPassword(byte[] input, String password) {
@Nullable KeyStrengthener keyStrengthener) {
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,
@@ -373,11 +366,8 @@ class CryptoComponentImpl implements CryptoComponent {
// Format version // Format version
byte formatVersion = input[inputOff]; byte formatVersion = input[inputOff];
inputOff++; inputOff++;
// Check whether we support this format version if (formatVersion != PBKDF_FORMAT_SCRYPT)
if (formatVersion != PBKDF_FORMAT_SCRYPT && return null; // Unknown format
formatVersion != PBKDF_FORMAT_SCRYPT_STRENGTHENED) {
return null;
}
// Salt // Salt
byte[] salt = new byte[PBKDF_SALT_BYTES]; byte[] salt = new byte[PBKDF_SALT_BYTES];
arraycopy(input, inputOff, salt, 0, salt.length); arraycopy(input, inputOff, salt, 0, salt.length);
@@ -391,13 +381,8 @@ class CryptoComponentImpl implements CryptoComponent {
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);
inputOff += iv.length; inputOff += iv.length;
// Derive the decryption key from the password // Derive the 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 (keyStrengthener == null || !keyStrengthener.isInitialised())
return null; // Can't derive the same strengthened key
key = keyStrengthener.strengthenKey(key);
}
// Initialise the cipher // Initialise the cipher
try { try {
cipher.init(false, key, iv); cipher.init(false, key, iv);
@@ -415,12 +400,6 @@ class CryptoComponentImpl implements CryptoComponent {
} }
} }
@Override
public boolean isEncryptedWithStrengthenedKey(byte[] ciphertext) {
return ciphertext.length > 0 &&
ciphertext[0] == PBKDF_FORMAT_SCRYPT_STRENGTHENED;
}
@Override @Override
public byte[] encryptToKey(PublicKey publicKey, byte[] plaintext) { public byte[] encryptToKey(PublicKey publicKey, byte[] plaintext) {
try { try {

View File

@@ -33,7 +33,6 @@ import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -116,7 +115,7 @@ interface Database<T> {
* if the message was created locally. * if the message was created locally.
*/ */
void addMessage(T txn, Message m, MessageState state, boolean shared, void addMessage(T txn, Message m, MessageState state, boolean shared,
boolean temporary, @Nullable ContactId sender) throws DbException; @Nullable ContactId sender) throws DbException;
/** /**
* Adds a dependency between two messages, where the dependent message is * Adds a dependency between two messages, where the dependent message is
@@ -267,16 +266,6 @@ interface Database<T> {
*/ */
Collection<ContactId> getContacts(T txn, AuthorId local) throws DbException; Collection<ContactId> getContacts(T txn, AuthorId local) throws DbException;
/**
* Returns the contact with the given {@code handshakePublicKey}
* for the given local pseudonym or {@code null} if none exists.
* <p/>
* Read-only.
*/
@Nullable
Contact getContact(T txn, PublicKey handshakePublicKey, AuthorId local)
throws DbException;
/** /**
* Returns the group with the given ID. * Returns the group with the given ID.
* <p/> * <p/>
@@ -539,13 +528,6 @@ interface Database<T> {
*/ */
Settings getSettings(T txn, String namespace) throws DbException; Settings getSettings(T txn, String namespace) throws DbException;
/**
* Returns the versions of the sync protocol supported by the given contact.
* <p/>
* Read-only.
*/
List<Byte> getSyncVersions(T txn, ContactId c) throws DbException;
/** /**
* Returns all transport keys for the given transport. * Returns all transport keys for the given transport.
* <p/> * <p/>
@@ -648,12 +630,6 @@ interface Database<T> {
*/ */
void removePendingContact(T txn, PendingContactId p) throws DbException; void removePendingContact(T txn, PendingContactId p) throws DbException;
/**
* Removes all temporary messages (and all associated state) from the
* database.
*/
void removeTemporaryMessages(T txn) throws DbException;
/** /**
* Removes a transport (and all associated state) from the database. * Removes a transport (and all associated state) from the database.
*/ */
@@ -695,11 +671,6 @@ interface Database<T> {
void setHandshakeKeyPair(T txn, AuthorId local, PublicKey publicKey, void setHandshakeKeyPair(T txn, AuthorId local, PublicKey publicKey,
PrivateKey privateKey) throws DbException; PrivateKey privateKey) throws DbException;
/**
* Marks the given message as permanent, i.e. not temporary.
*/
void setMessagePermanent(T txn, MessageId m) throws DbException;
/** /**
* Marks the given message as shared. * Marks the given message as shared.
*/ */
@@ -718,12 +689,6 @@ interface Database<T> {
void setReorderingWindow(T txn, KeySetId k, TransportId t, void setReorderingWindow(T txn, KeySetId k, TransportId t,
long timePeriod, long base, byte[] bitmap) throws DbException; long timePeriod, long base, byte[] bitmap) throws DbException;
/**
* Sets the versions of the sync protocol supported by the given contact.
*/
void setSyncVersions(T txn, ContactId c, List<Byte> supported)
throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */

View File

@@ -65,7 +65,6 @@ import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent; import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
import org.briarproject.bramble.api.sync.event.MessagesSentEvent; import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
import org.briarproject.bramble.api.sync.event.SyncVersionsUpdatedEvent;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.TransportKeySet; import org.briarproject.bramble.api.transport.TransportKeySet;
@@ -274,14 +273,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public void addLocalMessage(Transaction transaction, Message m, public void addLocalMessage(Transaction transaction, Message m,
Metadata meta, boolean shared, boolean temporary) Metadata meta, boolean shared) throws DbException {
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsGroup(txn, m.getGroupId())) if (!db.containsGroup(txn, m.getGroupId()))
throw new NoSuchGroupException(); throw new NoSuchGroupException();
if (!db.containsMessage(txn, m.getId())) { if (!db.containsMessage(txn, m.getId())) {
db.addMessage(txn, m, DELIVERED, shared, temporary, null); db.addMessage(txn, m, DELIVERED, shared, null);
transaction.attach(new MessageAddedEvent(m, null)); transaction.attach(new MessageAddedEvent(m, null));
transaction.attach(new MessageStateChangedEvent(m.getId(), true, transaction.attach(new MessageStateChangedEvent(m.getId(), true,
DELIVERED)); DELIVERED));
@@ -291,17 +289,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public void addPendingContact(Transaction transaction, PendingContact p, public void addPendingContact(Transaction transaction, PendingContact p)
AuthorId local) throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
Contact contact = db.getContact(txn, p.getPublicKey(), local); if (db.containsPendingContact(txn, p.getId()))
if (contact != null) throw new PendingContactExistsException();
throw new ContactExistsException(local, contact.getAuthor());
if (db.containsPendingContact(txn, p.getId())) {
PendingContact existing = db.getPendingContact(txn, p.getId());
throw new PendingContactExistsException(existing);
}
db.addPendingContact(txn, p); db.addPendingContact(txn, p);
transaction.attach(new PendingContactAddedEvent(p)); transaction.attach(new PendingContactAddedEvent(p));
} }
@@ -722,15 +715,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getSettings(txn, namespace); return db.getSettings(txn, namespace);
} }
@Override
public List<Byte> getSyncVersions(Transaction transaction, ContactId c)
throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
return db.getSyncVersions(txn, c);
}
@Override @Override
public Collection<TransportKeySet> getTransportKeys(Transaction transaction, public Collection<TransportKeySet> getTransportKeys(Transaction transaction,
TransportId t) throws DbException { TransportId t) throws DbException {
@@ -816,7 +800,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.raiseSeenFlag(txn, c, m.getId()); db.raiseSeenFlag(txn, c, m.getId());
db.raiseAckFlag(txn, c, m.getId()); db.raiseAckFlag(txn, c, m.getId());
} else { } else {
db.addMessage(txn, m, UNKNOWN, false, false, c); db.addMessage(txn, m, UNKNOWN, false, c);
transaction.attach(new MessageAddedEvent(m, c)); transaction.attach(new MessageAddedEvent(m, c));
} }
transaction.attach(new MessageToAckEvent(c)); transaction.attach(new MessageToAckEvent(c));
@@ -924,14 +908,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new PendingContactRemovedEvent(p)); transaction.attach(new PendingContactRemovedEvent(p));
} }
@Override
public void removeTemporaryMessages(Transaction transaction)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
db.removeTemporaryMessages(txn);
}
@Override @Override
public void removeTransport(Transaction transaction, TransportId t) public void removeTransport(Transaction transaction, TransportId t)
throws DbException { throws DbException {
@@ -991,16 +967,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new GroupVisibilityUpdatedEvent(affected)); transaction.attach(new GroupVisibilityUpdatedEvent(affected));
} }
@Override
public void setMessagePermanent(Transaction transaction, MessageId m)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
db.setMessagePermanent(txn, m);
}
@Override @Override
public void setMessageShared(Transaction transaction, MessageId m) public void setMessageShared(Transaction transaction, MessageId m)
throws DbException { throws DbException {
@@ -1009,7 +975,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (!db.containsMessage(txn, m)) if (!db.containsMessage(txn, m))
throw new NoSuchMessageException(); throw new NoSuchMessageException();
if (db.getMessageState(txn, m) != DELIVERED) if (db.getMessageState(txn, m) != DELIVERED)
throw new IllegalArgumentException("Shared undelivered message"); throw new IllegalArgumentException(
"Shared undelivered message");
db.setMessageShared(txn, m); db.setMessageShared(txn, m);
transaction.attach(new MessageSharedEvent(m)); transaction.attach(new MessageSharedEvent(m));
} }
@@ -1061,17 +1028,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.setReorderingWindow(txn, k, t, timePeriod, base, bitmap); db.setReorderingWindow(txn, k, t, timePeriod, base, bitmap);
} }
@Override
public void setSyncVersions(Transaction transaction, ContactId c,
List<Byte> supported) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
db.setSyncVersions(txn, c, supported);
transaction.attach(new SyncVersionsUpdatedEvent(c, supported));
}
@Override @Override
public void setTransportKeysActive(Transaction transaction, TransportId t, public void setTransportKeysActive(Transaction transaction, TransportId t,
KeySetId k) throws DbException { KeySetId k) throws DbException {

View File

@@ -62,7 +62,6 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import static java.sql.Types.BINARY; import static java.sql.Types.BINARY;
import static java.sql.Types.BOOLEAN; import static java.sql.Types.BOOLEAN;
@@ -98,7 +97,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
abstract class JdbcDatabase implements Database<Connection> { abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing // Package access for testing
static final int CODE_SCHEMA_VERSION = 47; static final int CODE_SCHEMA_VERSION = 45;
// Time period offsets for incoming transport keys // Time period offsets for incoming transport keys
private static final int OFFSET_PREV = -1; private static final int OFFSET_PREV = -1;
@@ -135,7 +134,6 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " handshakePublicKey _BINARY," // Null if key is unknown + " handshakePublicKey _BINARY," // Null if key is unknown
+ " localAuthorId _HASH NOT NULL," + " localAuthorId _HASH NOT NULL,"
+ " verified BOOLEAN NOT NULL," + " verified BOOLEAN NOT NULL,"
+ " syncVersions _BINARY DEFAULT '00' NOT NULL,"
+ " PRIMARY KEY (contactId)," + " PRIMARY KEY (contactId),"
+ " FOREIGN KEY (localAuthorId)" + " FOREIGN KEY (localAuthorId)"
+ " REFERENCES localAuthors (authorId)" + " REFERENCES localAuthors (authorId)"
@@ -179,7 +177,6 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " timestamp BIGINT NOT NULL," + " timestamp BIGINT NOT NULL,"
+ " state INT NOT NULL," + " state INT NOT NULL,"
+ " shared BOOLEAN NOT NULL," + " shared BOOLEAN NOT NULL,"
+ " temporary BOOLEAN NOT NULL,"
+ " length INT NOT NULL," + " length INT NOT NULL,"
+ " raw BLOB," // Null if message has been deleted + " raw BLOB," // Null if message has been deleted
+ " PRIMARY KEY (messageId)," + " PRIMARY KEY (messageId),"
@@ -339,26 +336,25 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final Logger LOG = private static final Logger LOG =
getLogger(JdbcDatabase.class.getName()); getLogger(JdbcDatabase.class.getName());
// Different database libraries use different names for certain types
private final MessageFactory messageFactory; private final MessageFactory messageFactory;
private final Clock clock; private final Clock clock;
private final DatabaseTypes dbTypes; private final DatabaseTypes dbTypes;
private final Lock connectionsLock = new ReentrantLock(); // Locking: connectionsLock
private final Condition connectionsChanged = connectionsLock.newCondition();
@GuardedBy("connectionsLock")
private final LinkedList<Connection> connections = new LinkedList<>(); private final LinkedList<Connection> connections = new LinkedList<>();
@GuardedBy("connectionsLock") private int openConnections = 0; // Locking: connectionsLock
private int openConnections = 0; private boolean closed = false; // Locking: connectionsLock
@GuardedBy("connectionsLock")
private boolean closed = false;
protected abstract Connection createConnection() protected abstract Connection createConnection()
throws DbException, SQLException; throws DbException, SQLException;
protected abstract void compactAndClose() throws DbException; protected abstract void compactAndClose() throws DbException;
private final Lock connectionsLock = new ReentrantLock();
private final Condition connectionsChanged = connectionsLock.newCondition();
JdbcDatabase(DatabaseTypes databaseTypes, MessageFactory messageFactory, JdbcDatabase(DatabaseTypes databaseTypes, MessageFactory messageFactory,
Clock clock) { Clock clock) {
this.dbTypes = databaseTypes; this.dbTypes = databaseTypes;
@@ -461,9 +457,7 @@ abstract class JdbcDatabase implements Database<Connection> {
new Migration41_42(dbTypes), new Migration41_42(dbTypes),
new Migration42_43(dbTypes), new Migration42_43(dbTypes),
new Migration43_44(dbTypes), new Migration43_44(dbTypes),
new Migration44_45(), new Migration44_45()
new Migration45_46(),
new Migration46_47(dbTypes)
); );
} }
@@ -783,23 +777,22 @@ abstract class JdbcDatabase implements Database<Connection> {
@Override @Override
public void addMessage(Connection txn, Message m, MessageState state, public void addMessage(Connection txn, Message m, MessageState state,
boolean shared, boolean temporary, @Nullable ContactId sender) boolean messageShared, @Nullable ContactId sender)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "INSERT INTO messages (messageId, groupId, timestamp," String sql = "INSERT INTO messages (messageId, groupId, timestamp,"
+ " state, shared, temporary, length, raw)" + " state, shared, length, raw)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getId().getBytes()); ps.setBytes(1, m.getId().getBytes());
ps.setBytes(2, m.getGroupId().getBytes()); ps.setBytes(2, m.getGroupId().getBytes());
ps.setLong(3, m.getTimestamp()); ps.setLong(3, m.getTimestamp());
ps.setInt(4, state.getValue()); ps.setInt(4, state.getValue());
ps.setBoolean(5, shared); ps.setBoolean(5, messageShared);
ps.setBoolean(6, temporary);
byte[] raw = messageFactory.getRawMessage(m); byte[] raw = messageFactory.getRawMessage(m);
ps.setInt(7, raw.length); ps.setInt(6, raw.length);
ps.setBytes(8, raw); ps.setBytes(7, raw);
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -811,7 +804,8 @@ abstract class JdbcDatabase implements Database<Connection> {
boolean offered = removeOfferedMessage(txn, c, m.getId()); boolean offered = removeOfferedMessage(txn, c, m.getId());
boolean seen = offered || c.equals(sender); boolean seen = offered || c.equals(sender);
addStatus(txn, m.getId(), c, m.getGroupId(), m.getTimestamp(), addStatus(txn, m.getId(), c, m.getGroupId(), m.getTimestamp(),
raw.length, state, e.getValue(), shared, false, seen); raw.length, state, e.getValue(), messageShared,
false, seen);
} }
// Update denormalised column in messageDependencies if dependency // Update denormalised column in messageDependencies if dependency
// is in same group as dependent // is in same group as dependent
@@ -1465,47 +1459,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Nullable
@Override
public Contact getContact(Connection txn, PublicKey handshakePublicKey,
AuthorId localAuthorId) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT contactId, authorId, formatVersion, name,"
+ " alias, publicKey, verified"
+ " FROM contacts"
+ " WHERE handshakePublicKey = ? AND localAuthorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, handshakePublicKey.getEncoded());
ps.setBytes(2, localAuthorId.getBytes());
rs = ps.executeQuery();
if (!rs.next()) {
rs.close();
ps.close();
return null;
}
ContactId contactId = new ContactId(rs.getInt(1));
AuthorId authorId = new AuthorId(rs.getBytes(2));
int formatVersion = rs.getInt(3);
String name = rs.getString(4);
String alias = rs.getString(5);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(6));
boolean verified = rs.getBoolean(7);
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
Author author =
new Author(authorId, formatVersion, name, publicKey);
return new Contact(contactId, author, localAuthorId, alias,
handshakePublicKey, verified);
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public Group getGroup(Connection txn, GroupId g) throws DbException { public Group getGroup(Connection txn, GroupId g) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -2371,32 +2324,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public List<Byte> getSyncVersions(Connection txn, ContactId c)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT syncVersions FROM contacts"
+ " WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
byte[] bytes = rs.getBytes(1);
List<Byte> supported = new ArrayList<>(bytes.length);
for (byte b : bytes) supported.add(b);
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
return supported;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public Collection<TransportKeySet> getTransportKeys(Connection txn, public Collection<TransportKeySet> getTransportKeys(Connection txn,
TransportId t) throws DbException { TransportId t) throws DbException {
@@ -2949,21 +2876,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void removeTemporaryMessages(Connection txn) throws DbException {
Statement s = null;
try {
String sql = "DELETE FROM messages WHERE temporary = TRUE";
s = txn.createStatement();
int affected = s.executeUpdate(sql);
if (affected < 0) throw new DbStateException();
s.close();
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void removeTransport(Connection txn, TransportId t) public void removeTransport(Connection txn, TransportId t)
throws DbException { throws DbException {
@@ -3109,24 +3021,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void setMessagePermanent(Connection txn, MessageId m)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE messages SET temporary = FALSE"
+ " WHERE messageId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void setMessageShared(Connection txn, MessageId m) public void setMessageShared(Connection txn, MessageId m)
throws DbException { throws DbException {
@@ -3230,29 +3124,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void setSyncVersions(Connection txn, ContactId c,
List<Byte> supported) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE contacts SET syncVersions = ?"
+ " WHERE contactId = ?";
ps = txn.prepareStatement(sql);
byte[] bytes = new byte[supported.size()];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = supported.get(i);
}
ps.setBytes(1, bytes);
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void setTransportKeysActive(Connection txn, TransportId t, public void setTransportKeysActive(Connection txn, TransportId t,
KeySetId k) throws DbException { KeySetId k) throws DbException {

View File

@@ -1,40 +0,0 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration45_46 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration45_46.class.getName());
@Override
public int getStartVersion() {
return 45;
}
@Override
public int getEndVersion() {
return 46;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute("ALTER TABLE messages"
+ " ADD COLUMN temporary BOOLEAN DEFAULT FALSE NOT NULL");
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -1,47 +0,0 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration46_47 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration46_47.class.getName());
private final DatabaseTypes dbTypes;
Migration46_47(DatabaseTypes dbTypes) {
this.dbTypes = dbTypes;
}
@Override
public int getStartVersion() {
return 46;
}
@Override
public int getEndVersion() {
return 47;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute(dbTypes.replaceTypes("ALTER TABLE contacts"
+ " ADD COLUMN syncVersions"
+ " _BINARY DEFAULT '00' NOT NULL"));
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -107,11 +107,8 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
else logDuration(LOG, "Creating database", start); else logDuration(LOG, "Creating database", start);
db.transaction(false, txn -> { db.transaction(false, txn -> {
long start1 = now();
db.removeTemporaryMessages(txn);
logDuration(LOG, "Removing temporary messages", start1);
for (OpenDatabaseHook hook : openDatabaseHooks) { for (OpenDatabaseHook hook : openDatabaseHooks) {
start1 = now(); long start1 = now();
hook.onDatabaseOpened(txn); hook.onDatabaseOpened(txn);
if (LOG.isLoggable(FINE)) { if (LOG.isLoggable(FINE)) {
logDuration(LOG, "Calling open database hook " logDuration(LOG, "Calling open database hook "

View File

@@ -8,7 +8,6 @@ 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.ConnectionManager;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.Plugin.State;
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;
@@ -19,9 +18,8 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; 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 org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
@@ -38,7 +36,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
@@ -48,9 +45,6 @@ import static java.util.logging.Level.FINE;
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;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.Plugin.PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@@ -183,26 +177,6 @@ class PluginManagerImpl implements PluginManager, Service {
return supported; return supported;
} }
@Override
public void setPluginEnabled(TransportId t, boolean enabled) {
Plugin plugin = plugins.get(t);
if (plugin == null) return;
Settings s = new Settings();
s.putBoolean(PREF_PLUGIN_ENABLE, enabled);
ioExecutor.execute(() -> mergeSettings(s, t.getString()));
}
private void mergeSettings(Settings s, String namespace) {
try {
long start = now();
settingsManager.mergeSettings(s, namespace);
logDuration(LOG, "Merging settings", start);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
}
private class PluginStarter implements Runnable { private class PluginStarter implements Runnable {
private final Plugin plugin; private final Plugin plugin;
@@ -276,8 +250,7 @@ class PluginManagerImpl implements PluginManager, Service {
private class Callback implements PluginCallback { private class Callback implements PluginCallback {
private final TransportId id; private final TransportId id;
private final AtomicReference<State> state = private final AtomicBoolean enabled = new AtomicBoolean(false);
new AtomicReference<>(STARTING_STOPPING);
private Callback(TransportId id) { private Callback(TransportId id) {
this.id = id; this.id = id;
@@ -305,7 +278,11 @@ class PluginManagerImpl implements PluginManager, Service {
@Override @Override
public void mergeSettings(Settings s) { public void mergeSettings(Settings s) {
PluginManagerImpl.this.mergeSettings(s, id.getString()); try {
settingsManager.mergeSettings(s, id.getString());
} catch (DbException e) {
logException(LOG, WARNING, e);
}
} }
@Override @Override
@@ -318,20 +295,15 @@ class PluginManagerImpl implements PluginManager, Service {
} }
@Override @Override
public void pluginStateChanged(State newState) { public void transportEnabled() {
State oldState = state.getAndSet(newState); if (!enabled.getAndSet(true))
if (newState != oldState) { eventBus.broadcast(new TransportEnabledEvent(id));
if (LOG.isLoggable(INFO)) { }
LOG.info(id + " changed from state " + oldState
+ " to " + newState); @Override
} public void transportDisabled() {
eventBus.broadcast(new TransportStateEvent(id, newState)); if (enabled.getAndSet(false))
if (newState == ACTIVE) { eventBus.broadcast(new TransportDisabledEvent(id));
eventBus.broadcast(new TransportActiveEvent(id));
} else if (oldState == ACTIVE) {
eventBus.broadcast(new TransportInactiveEvent(id));
}
}
} }
@Override @Override

View File

@@ -11,7 +11,6 @@ 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.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.DiscoveryHandler;
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;
@@ -21,8 +20,8 @@ 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.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.TransportActiveEvent; import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
@@ -33,7 +32,6 @@ import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -108,13 +106,13 @@ class PollerImpl implements Poller, EventListener {
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e; ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
// Reschedule polling, the polling interval may have decreased // Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId()); reschedule(c.getTransportId());
} else if (e instanceof TransportActiveEvent) { } else if (e instanceof TransportEnabledEvent) {
TransportActiveEvent t = (TransportActiveEvent) e; TransportEnabledEvent t = (TransportEnabledEvent) e;
// Poll the newly activated transport // Poll the newly enabled transport
pollNow(t.getTransportId()); pollNow(t.getTransportId());
} else if (e instanceof TransportInactiveEvent) { } else if (e instanceof TransportDisabledEvent) {
TransportInactiveEvent t = (TransportInactiveEvent) e; TransportDisabledEvent t = (TransportDisabledEvent) e;
// Cancel polling for the deactivated transport // Cancel polling for the disabled transport
cancel(t.getTransportId()); cancel(t.getTransportId());
} }
} }
@@ -212,20 +210,18 @@ class PollerImpl implements Poller, EventListener {
@IoExecutor @IoExecutor
private void poll(Plugin p) { private void poll(Plugin p) {
TransportId t = p.getId(); TransportId t = p.getId();
if (LOG.isLoggable(INFO)) LOG.info("Polling " + t); if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t);
try { try {
Map<ContactId, TransportProperties> remote = Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(t); transportPropertyManager.getRemoteProperties(t);
Collection<ContactId> connected = Collection<ContactId> connected =
connectionRegistry.getConnectedContacts(t); connectionRegistry.getConnectedContacts(t);
List<Pair<TransportProperties, ConnectionHandler>> properties = Collection<Pair<TransportProperties, ConnectionHandler>>
new ArrayList<>(); properties = new ArrayList<>();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) { for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey(); ContactId c = e.getKey();
if (!connected.contains(c)) { if (!connected.contains(c))
ConnHandler handler = new ConnHandler(c, t); properties.add(new Pair<>(e.getValue(), new Handler(c, t)));
properties.add(new Pair<>(e.getValue(), handler));
}
} }
if (!properties.isEmpty()) p.poll(properties); if (!properties.isEmpty()) p.poll(properties);
} catch (DbException e) { } catch (DbException e) {
@@ -233,30 +229,6 @@ class PollerImpl implements Poller, EventListener {
} }
} }
@IoExecutor
private void discover(DuplexPlugin p) {
TransportId t = p.getId();
if (LOG.isLoggable(INFO)) LOG.info("Discovering peers for " + t);
try {
Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(t);
Collection<ContactId> connected =
connectionRegistry.getConnectedContacts(t);
List<Pair<TransportProperties, DiscoveryHandler>> properties =
new ArrayList<>();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (!connected.contains(c)) {
DiscoHandler handler = new DiscoHandler(c, p);
properties.add(new Pair<>(e.getValue(), handler));
}
}
if (!properties.isEmpty()) p.discoverPeers(properties);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
}
private class ScheduledPollTask { private class ScheduledPollTask {
private final PollTask task; private final PollTask task;
@@ -296,23 +268,16 @@ class PollerImpl implements Poller, EventListener {
int delay = plugin.getPollingInterval(); int delay = plugin.getPollingInterval();
if (randomiseNext) delay = (int) (delay * random.nextDouble()); if (randomiseNext) delay = (int) (delay * random.nextDouble());
schedule(plugin, delay, false); schedule(plugin, delay, false);
// FIXME: Revert poll(plugin);
if (plugin instanceof DuplexPlugin) {
DuplexPlugin d = (DuplexPlugin) plugin;
if (d.supportsDiscovery()) discover(d);
else poll(d);
} else {
poll(plugin);
}
} }
} }
private class ConnHandler implements ConnectionHandler { private class Handler implements ConnectionHandler {
private final ContactId contactId; private final ContactId contactId;
private final TransportId transportId; private final TransportId transportId;
private ConnHandler(ContactId contactId, TransportId transportId) { private Handler(ContactId contactId, TransportId transportId) {
this.contactId = contactId; this.contactId = contactId;
this.transportId = transportId; this.transportId = transportId;
} }
@@ -335,27 +300,4 @@ class PollerImpl implements Poller, EventListener {
transportId, w); transportId, w);
} }
} }
private class DiscoHandler implements DiscoveryHandler {
private final ContactId contactId;
private final DuplexPlugin plugin;
private DiscoHandler(ContactId contactId, DuplexPlugin plugin) {
this.contactId = contactId;
this.plugin = plugin;
}
@Override
public void handleDevice(TransportProperties p) {
LOG.info("Discovered contact via " + plugin.getId());
ioExecutor.execute(() -> {
DuplexTransportConnection c = plugin.createConnection(p);
if (c != null) {
connectionManager.manageOutgoingConnection(contactId,
plugin.getId(), c);
}
});
}
}
} }

View File

@@ -9,13 +9,10 @@ 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;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStoppedListeningEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStoppedListeningEvent;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
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;
import org.briarproject.bramble.api.plugin.ConnectionHandler; import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.DiscoveryHandler;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginException; import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -32,28 +29,23 @@ import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import java.io.IOException; import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.List; import java.util.Collection;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
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;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID; import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES; import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@@ -76,9 +68,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
private final int maxLatency; private final int maxLatency;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
protected final PluginState state = new PluginState(); private volatile boolean running = false, contactConnections = false;
private volatile String contactConnectionsUuid = null; private volatile String contactConnectionsUuid = null;
private volatile SS socket = null;
abstract void initialiseAdapter() throws IOException; abstract void initialiseAdapter() throws IOException;
@@ -127,18 +119,14 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
LOG.info("Bluetooth enabled"); LOG.info("Bluetooth enabled");
// We may not have been able to get the local address before // We may not have been able to get the local address before
ioExecutor.execute(this::updateProperties); ioExecutor.execute(this::updateProperties);
if (getState() == INACTIVE) bind(); if (shouldAllowContactConnections()) bind();
} }
void onAdapterDisabled() { void onAdapterDisabled() {
LOG.info("Bluetooth disabled"); LOG.info("Bluetooth disabled");
tryToClose(socket);
connectionLimiter.allConnectionsClosed(); connectionLimiter.allConnectionsClosed();
// The server socket may not have been closed automatically callback.transportDisabled();
SS ss = state.clearServerSocket();
if (ss != null) {
LOG.info("Closing server socket");
tryToClose(ss);
}
} }
@Override @Override
@@ -160,24 +148,31 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override @Override
public void start() throws PluginException { public void start() throws PluginException {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
Settings settings = callback.getSettings();
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false);
state.setStarted(enabledByUser);
try { try {
initialiseAdapter(); initialiseAdapter();
} catch (IOException e) { } catch (IOException e) {
throw new PluginException(e); throw new PluginException(e);
} }
updateProperties(); updateProperties();
if (enabledByUser) { running = true;
loadSettings(callback.getSettings());
if (shouldAllowContactConnections()) {
if (isAdapterEnabled()) bind(); if (isAdapterEnabled()) bind();
else enableAdapter(); else enableAdapter();
} }
} }
private void loadSettings(Settings settings) {
contactConnections = settings.getBoolean(PREF_BT_ENABLE, false);
}
private boolean shouldAllowContactConnections() {
return contactConnections;
}
private void bind() { private void bind() {
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
if (getState() != INACTIVE) return; if (!isRunning() || !shouldAllowContactConnections()) return;
// Bind a server socket to accept connections from contacts // Bind a server socket to accept connections from contacts
SS ss; SS ss;
try { try {
@@ -186,13 +181,14 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
return; return;
} }
if (!state.setServerSocket(ss)) { if (!isRunning() || !shouldAllowContactConnections()) {
LOG.info("Closing redundant server socket");
tryToClose(ss); tryToClose(ss);
return; return;
} }
socket = ss;
backoff.reset(); backoff.reset();
acceptContactConnections(ss); callback.transportEnabled();
acceptContactConnections();
}); });
} }
@@ -221,39 +217,34 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (changed) callback.mergeLocalProperties(p); if (changed) callback.mergeLocalProperties(p);
} }
private void acceptContactConnections(SS ss) { private void acceptContactConnections() {
while (true) { while (true) {
DuplexTransportConnection conn; DuplexTransportConnection conn;
try { try {
conn = acceptConnection(ss); conn = acceptConnection(socket);
} catch (IOException e) { } catch (IOException e) {
// This is expected when the server socket is closed // This is expected when the socket is closed
LOG.info("Server socket closed"); if (LOG.isLoggable(INFO)) LOG.info(e.toString());
state.clearServerSocket();
return; return;
} }
LOG.info("Connection received");
backoff.reset(); backoff.reset();
if (connectionLimiter.contactConnectionOpened(conn)) if (connectionLimiter.contactConnectionOpened(conn))
callback.handleConnection(conn); callback.handleConnection(conn);
if (!running) return;
} }
} }
@Override @Override
public void stop() { public void stop() {
SS ss = state.setStopped(); running = false;
tryToClose(ss); tryToClose(socket);
callback.transportDisabled();
disableAdapterIfEnabledByUs(); disableAdapterIfEnabledByUs();
} }
@Override @Override
public State getState() { public boolean isRunning() {
return state.getState(); return running && isAdapterEnabled();
}
@Override
public int getReasonsDisabled() {
return state.getReasonsDisabled();
} }
@Override @Override
@@ -267,9 +258,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
} }
@Override @Override
public void poll( public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
List<Pair<TransportProperties, ConnectionHandler>> properties) { properties) {
if (getState() != ACTIVE) return; if (!isRunning() || !shouldAllowContactConnections()) return;
backoff.increment(); backoff.increment();
for (Pair<TransportProperties, ConnectionHandler> p : properties) { for (Pair<TransportProperties, ConnectionHandler> p : properties) {
connect(p.getFirst(), p.getSecond()); connect(p.getFirst(), p.getSecond());
@@ -282,7 +273,7 @@ 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 (getState() != ACTIVE) return; if (!isRunning() || !shouldAllowContactConnections()) return;
if (!connectionLimiter.canOpenContactConnection()) return; if (!connectionLimiter.canOpenContactConnection()) return;
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
@@ -326,7 +317,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override @Override
public DuplexTransportConnection createConnection(TransportProperties p) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (getState() != ACTIVE) return null; if (!isRunning() || !shouldAllowContactConnections()) return null;
if (!connectionLimiter.canOpenContactConnection()) return null; if (!connectionLimiter.canOpenContactConnection()) return null;
String address = p.get(PROP_ADDRESS); String address = p.get(PROP_ADDRESS);
if (isNullOrEmpty(address)) return null; if (isNullOrEmpty(address)) return null;
@@ -345,7 +336,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override @Override
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
if (getState() != ACTIVE) return null; if (!isRunning()) return null;
// No truncation necessary because COMMIT_LENGTH = 16 // No truncation necessary because COMMIT_LENGTH = 16
String uuid = UUID.nameUUIDFromBytes(commitment).toString(); String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid); if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
@@ -357,7 +348,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
return null; return null;
} }
if (getState() != ACTIVE) { if (!isRunning()) {
tryToClose(ss); tryToClose(ss);
return null; return null;
} }
@@ -371,7 +362,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override @Override
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor) { byte[] commitment, BdfList descriptor) {
if (getState() != ACTIVE) return null; if (!isRunning()) return null;
// No truncation necessary because COMMIT_LENGTH = 16 // No truncation necessary because COMMIT_LENGTH = 16
String uuid = UUID.nameUUIDFromBytes(commitment).toString(); String uuid = UUID.nameUUIDFromBytes(commitment).toString();
DuplexTransportConnection conn; DuplexTransportConnection conn;
@@ -412,17 +403,6 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public boolean supportsDiscovery() {
return false;
}
@Override
public void discoverPeers(
List<Pair<TransportProperties, DiscoveryHandler>> properties) {
throw new UnsupportedOperationException();
}
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof EnableBluetoothEvent) { if (e instanceof EnableBluetoothEvent) {
@@ -442,17 +422,17 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
} }
} }
@IoExecutor
private void onSettingsUpdated(Settings settings) { private void onSettingsUpdated(Settings settings) {
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false); boolean wasAllowed = shouldAllowContactConnections();
SS ss = state.setEnabledByUser(enabledByUser); loadSettings(settings);
State s = getState(); boolean isAllowed = shouldAllowContactConnections();
if (ss != null) { if (wasAllowed && !isAllowed) {
LOG.info("Disabled by user, closing server socket"); LOG.info("Contact connections disabled");
tryToClose(ss); tryToClose(socket);
callback.transportDisabled();
disableAdapterIfEnabledByUs(); disableAdapterIfEnabledByUs();
} else if (s == INACTIVE) { } else if (!wasAllowed && isAllowed) {
LOG.info("Enabled by user, opening server socket"); LOG.info("Contact connections enabled");
if (isAdapterEnabled()) bind(); if (isAdapterEnabled()) bind();
else enableAdapter(); else enableAdapter();
} }
@@ -480,70 +460,4 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
tryToClose(ss); tryToClose(ss);
} }
} }
@ThreadSafe
@NotNullByDefault
protected class PluginState {
@GuardedBy("this")
private boolean started = false,
stopped = false,
enabledByUser = false;
@GuardedBy("this")
@Nullable
private SS serverSocket = null;
synchronized void setStarted(boolean enabledByUser) {
started = true;
this.enabledByUser = enabledByUser;
callback.pluginStateChanged(getState());
}
@Nullable
synchronized SS setStopped() {
stopped = true;
SS ss = serverSocket;
serverSocket = null;
callback.pluginStateChanged(getState());
return ss;
}
@Nullable
synchronized SS setEnabledByUser(boolean enabledByUser) {
this.enabledByUser = enabledByUser;
SS ss = null;
if (!enabledByUser) {
ss = serverSocket;
serverSocket = null;
}
callback.pluginStateChanged(getState());
return ss;
}
synchronized boolean setServerSocket(SS ss) {
if (stopped || serverSocket != null) return false;
serverSocket = ss;
callback.pluginStateChanged(getState());
return true;
}
@Nullable
synchronized SS clearServerSocket() {
SS ss = serverSocket;
serverSocket = null;
callback.pluginStateChanged(getState());
return ss;
}
synchronized State getState() {
if (!started || stopped) return STARTING_STOPPING;
if (!enabledByUser) return DISABLED;
return serverSocket == null ? INACTIVE : ACTIVE;
}
synchronized int getReasonsDisabled() {
return getState() == DISABLED ? REASON_USER : 0;
}
}
} }

View File

@@ -16,7 +16,6 @@ import java.util.logging.Logger;
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.api.plugin.FileConstants.PROP_PATH; import static org.briarproject.bramble.api.plugin.FileConstants.PROP_PATH;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@@ -46,7 +45,7 @@ abstract class FilePlugin implements SimplexPlugin {
@Override @Override
public TransportConnectionReader createReader(TransportProperties p) { public TransportConnectionReader createReader(TransportProperties p) {
if (getState() != ACTIVE) return null; if (!isRunning()) return null;
String path = p.get(PROP_PATH); String path = p.get(PROP_PATH);
if (isNullOrEmpty(path)) return null; if (isNullOrEmpty(path)) return null;
try { try {
@@ -61,7 +60,7 @@ abstract class FilePlugin implements SimplexPlugin {
@Override @Override
public TransportConnectionWriter createWriter(TransportProperties p) { public TransportConnectionWriter createWriter(TransportProperties p) {
if (getState() != ACTIVE) return null; if (!isRunning()) return null;
String path = p.get(PROP_PATH); String path = p.get(PROP_PATH);
if (isNullOrEmpty(path)) return null; if (isNullOrEmpty(path)) return null;
try { try {

View File

@@ -11,25 +11,24 @@ import org.briarproject.bramble.api.plugin.TransportId;
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.util.IoUtils;
import java.io.IOException; 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;
@@ -38,9 +37,7 @@ 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.IoUtils.tryToClose;
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;
import static org.briarproject.bramble.util.StringUtils.join; import static org.briarproject.bramble.util.StringUtils.join;
@@ -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,83 +63,38 @@ class LanTcpPlugin extends TcpPlugin {
return ID; return ID;
} }
@Override
public void start() {
if (used.getAndSet(true)) throw new IllegalStateException();
initialisePortProperty();
Settings settings = callback.getSettings();
state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, false));
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);
@@ -201,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) {
@@ -227,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;
@@ -272,10 +209,10 @@ class LanTcpPlugin extends TcpPlugin {
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Failed to bind " + scrubSocketAddress(addr)); LOG.info("Failed to bind " + scrubSocketAddress(addr));
tryToClose(ss, LOG, WARNING); tryToClose(ss);
} }
} }
if (ss == null) { if (ss == null || !ss.isBound()) {
LOG.info("Could not bind server socket for key agreement"); LOG.info("Could not bind server socket for key agreement");
return null; return null;
} }
@@ -291,13 +228,7 @@ class LanTcpPlugin extends TcpPlugin {
@Override @Override
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor) { byte[] commitment, BdfList descriptor) {
ServerSocket ss = state.getServerSocket(); if (!isRunning()) return null;
if (ss == null) return null;
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);
@@ -305,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;
} }
@@ -317,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));
@@ -364,7 +296,22 @@ class LanTcpPlugin extends TcpPlugin {
@Override @Override
public void close() { public void close() {
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

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
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.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -19,21 +18,18 @@ 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;
private final EventBus eventBus;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
public LanTcpPluginFactory(Executor ioExecutor, EventBus eventBus, public LanTcpPluginFactory(Executor ioExecutor,
BackoffFactory backoffFactory) { BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.eventBus = eventBus;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
} }
@@ -51,9 +47,7 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(PluginCallback callback) { public DuplexPlugin createPlugin(PluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback, MAX_LATENCY, return new LanTcpPlugin(ioExecutor, backoff, callback, MAX_LATENCY,
MAX_IDLE_TIME, CONNECTION_TIMEOUT); MAX_IDLE_TIME);
eventBus.addListener(plugin);
return plugin;
} }
} }

View File

@@ -2,31 +2,27 @@ package org.briarproject.bramble.plugin.tcp;
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.event.Event; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
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;
import org.briarproject.bramble.api.plugin.ConnectionHandler; import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.DiscoveryHandler;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; 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.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
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;
@@ -39,26 +35,20 @@ import java.util.logging.Logger;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
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;
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.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
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;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
abstract class TcpPlugin implements DuplexPlugin, EventListener { abstract class TcpPlugin implements DuplexPlugin {
private static final Logger LOG = getLogger(TcpPlugin.class.getName()); private static final Logger LOG = getLogger(TcpPlugin.class.getName());
@@ -68,10 +58,11 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
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 final PluginState state = new PluginState();
protected volatile boolean running = false;
protected volatile ServerSocket socket = null;
/** /**
* Returns zero or more socket addresses on which the plugin should listen, * Returns zero or more socket addresses on which the plugin should listen,
@@ -95,18 +86,15 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
/** /**
* 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;
@@ -127,14 +115,14 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
@Override @Override
public void start() { public void start() {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
Settings settings = callback.getSettings(); running = true;
state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE, false));
bind(); bind();
} }
protected void bind() { protected void bind() {
bindExecutor.execute(() -> { bindExecutor.execute(() -> {
if (getState() != INACTIVE) return; if (!running) return;
if (socket != null && !socket.isClosed()) return;
ServerSocket ss = null; ServerSocket ss = null;
for (InetSocketAddress addr : getLocalSocketAddresses()) { for (InetSocketAddress addr : getLocalSocketAddresses()) {
try { try {
@@ -144,28 +132,34 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Failed to bind " + scrubSocketAddress(addr)); LOG.info("Failed to bind " + scrubSocketAddress(addr));
tryToClose(ss, LOG, WARNING); tryToClose(ss);
} }
} }
if (ss == null) { if (ss == null || !ss.isBound()) {
LOG.info("Could not bind server socket"); LOG.info("Could not bind server socket");
return; return;
} }
if (!state.setServerSocket(ss)) { if (!running) {
LOG.info("Closing redundant server socket"); tryToClose(ss);
tryToClose(ss, LOG, WARNING);
return; return;
} }
socket = ss;
backoff.reset(); backoff.reset();
InetSocketAddress local = InetSocketAddress local =
(InetSocketAddress) ss.getLocalSocketAddress(); (InetSocketAddress) ss.getLocalSocketAddress();
setLocalSocketAddress(local); setLocalSocketAddress(local);
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Listening on " + scrubSocketAddress(local)); LOG.info("Listening on " + scrubSocketAddress(local));
acceptContactConnections(ss); callback.transportEnabled();
acceptContactConnections();
}); });
} }
protected void tryToClose(@Nullable ServerSocket ss) {
IoUtils.tryToClose(ss, LOG, WARNING);
callback.transportDisabled();
}
String getIpPortString(InetSocketAddress a) { String getIpPortString(InetSocketAddress a) {
String addr = a.getAddress().getHostAddress(); String addr = a.getAddress().getHostAddress();
int percent = addr.indexOf('%'); int percent = addr.indexOf('%');
@@ -173,16 +167,15 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
return addr + ":" + a.getPort(); return addr + ":" + a.getPort();
} }
private void acceptContactConnections(ServerSocket ss) { private void acceptContactConnections() {
while (true) { while (isRunning()) {
Socket s; Socket s;
try { try {
s = ss.accept(); s = socket.accept();
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
} catch (IOException e) { } catch (IOException e) {
// This is expected when the server socket is closed // This is expected when the socket is closed
LOG.info("Server socket closed"); if (LOG.isLoggable(INFO)) LOG.info(e.toString());
state.clearServerSocket(ss);
return; return;
} }
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -195,18 +188,13 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
@Override @Override
public void stop() { public void stop() {
ServerSocket ss = state.setStopped(); running = false;
tryToClose(ss, LOG, WARNING); tryToClose(socket);
} }
@Override @Override
public State getState() { public boolean isRunning() {
return state.getState(); return running && socket != null && !socket.isClosed();
}
@Override
public int getReasonsDisabled() {
return state.getReasonsDisabled();
} }
@Override @Override
@@ -220,9 +208,9 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
} }
@Override @Override
public void poll( public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
List<Pair<TransportProperties, ConnectionHandler>> properties) { properties) {
if (getState() != ACTIVE) return; if (!isRunning()) return;
backoff.increment(); backoff.increment();
for (Pair<TransportProperties, ConnectionHandler> p : properties) { for (Pair<TransportProperties, ConnectionHandler> p : properties) {
connect(p.getFirst(), p.getSecond()); connect(p.getFirst(), p.getSecond());
@@ -241,24 +229,14 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
@Override @Override
public DuplexTransportConnection createConnection(TransportProperties p) { public DuplexTransportConnection createConnection(TransportProperties p) {
ServerSocket ss = state.getServerSocket(); if (!isRunning()) return null;
if (ss == null) return null;
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;
} }
@@ -266,8 +244,8 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
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));
@@ -281,19 +259,6 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
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();
} }
@@ -322,6 +287,22 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
} }
} }
@Override
public boolean supportsKeyAgreement() {
return false;
}
@Override
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
throw new UnsupportedOperationException();
}
@Override
public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor) {
throw new UnsupportedOperationException();
}
@Override @Override
public boolean supportsRendezvous() { public boolean supportsRendezvous() {
return false; return false;
@@ -333,130 +314,17 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override Collection<InetAddress> getLocalIpAddresses() {
public boolean supportsDiscovery() {
return false;
}
@Override
public void discoverPeers(
List<Pair<TransportProperties, DiscoveryHandler>> properties) {
throw new UnsupportedOperationException();
}
List<InterfaceAddress> getLocalInterfaceAddresses() {
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();
} }
} }
@Override
public void eventOccurred(Event e) {
if (e instanceof SettingsUpdatedEvent) {
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
if (s.getNamespace().equals(getId().getString()))
ioExecutor.execute(() -> onSettingsUpdated(s.getSettings()));
}
}
@IoExecutor
private void onSettingsUpdated(Settings settings) {
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false);
ServerSocket ss = state.setEnabledByUser(enabledByUser);
State s = getState();
if (ss != null) {
LOG.info("Disabled by user, closing server socket");
tryToClose(ss, LOG, WARNING);
} else if (s == INACTIVE) {
LOG.info("Enabled by user, opening server socket");
bind();
}
}
@ThreadSafe
@NotNullByDefault
protected class PluginState {
@GuardedBy("this")
private boolean started = false, stopped = false, enabledByUser = false;
@GuardedBy("this")
@Nullable
private ServerSocket serverSocket = null;
synchronized void setStarted(boolean enabledByUser) {
started = true;
this.enabledByUser = enabledByUser;
callback.pluginStateChanged(getState());
}
@Nullable
synchronized ServerSocket setStopped() {
stopped = true;
ServerSocket ss = serverSocket;
serverSocket = null;
callback.pluginStateChanged(getState());
return ss;
}
@Nullable
synchronized ServerSocket setEnabledByUser(boolean enabledByUser) {
this.enabledByUser = enabledByUser;
ServerSocket ss = null;
if (!enabledByUser) {
ss = serverSocket;
serverSocket = null;
}
callback.pluginStateChanged(getState());
return ss;
}
@Nullable
synchronized ServerSocket getServerSocket() {
return serverSocket;
}
synchronized boolean setServerSocket(ServerSocket ss) {
if (stopped || serverSocket != null) return false;
serverSocket = ss;
callback.pluginStateChanged(getState());
return true;
}
synchronized void clearServerSocket(ServerSocket ss) {
if (serverSocket == ss) serverSocket = null;
callback.pluginStateChanged(getState());
}
synchronized State getState() {
if (!started || stopped) return STARTING_STOPPING;
if (!enabledByUser) return DISABLED;
return serverSocket == null ? INACTIVE : ACTIVE;
}
synchronized int getReasonsDisabled() {
return getState() == DISABLED ? REASON_USER : 0;
}
}
} }

View File

@@ -1,19 +1,15 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
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;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TransportId; 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.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;
@@ -33,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;
} }
@@ -45,29 +39,13 @@ class WanTcpPlugin extends TcpPlugin {
return ID; return ID;
} }
@Override
public boolean supportsKeyAgreement() {
return false;
}
@Override
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
throw new UnsupportedOperationException();
}
@Override
public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor) {
throw new UnsupportedOperationException();
}
@Override @Override
protected List<InetSocketAddress> getLocalSocketAddresses() { protected List<InetSocketAddress> getLocalSocketAddresses() {
// Use the same address and port as last time if available // Use the same address and port as last time if available
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))
@@ -108,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

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.event.EventBus;
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.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
@@ -20,22 +19,19 @@ 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;
private final EventBus eventBus;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
private final ShutdownManager shutdownManager; private final ShutdownManager shutdownManager;
public WanTcpPluginFactory(Executor ioExecutor, EventBus eventBus, public WanTcpPluginFactory(Executor ioExecutor,
BackoffFactory backoffFactory, ShutdownManager shutdownManager) { BackoffFactory backoffFactory, ShutdownManager shutdownManager) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.eventBus = eventBus;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
this.shutdownManager = shutdownManager; this.shutdownManager = shutdownManager;
} }
@@ -54,10 +50,8 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(PluginCallback callback) { public DuplexPlugin createPlugin(PluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
WanTcpPlugin plugin = 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);
eventBus.addListener(plugin);
return plugin;
} }
} }

View File

@@ -17,7 +17,7 @@ public interface CircumventionProvider {
String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE"}; String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE"};
/** /**
* Countries where obfs4 or meek bridge connections are likely to work. * Countries where obfs4 bridge connection are likely to work.
* Should be a subset of {@link #BLOCKED}. * Should be a subset of {@link #BLOCKED}.
*/ */
String[] BRIDGES = { "CN", "IR", "EG", "BY", "TR", "SY", "VE" }; String[] BRIDGES = { "CN", "IR", "EG", "BY", "TR", "SY", "VE" };

View File

@@ -15,11 +15,9 @@ import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.network.NetworkStatus; import org.briarproject.bramble.api.network.NetworkStatus;
import org.briarproject.bramble.api.network.event.NetworkStatusEvent; import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
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;
import org.briarproject.bramble.api.plugin.ConnectionHandler; import org.briarproject.bramble.api.plugin.ConnectionHandler;
import org.briarproject.bramble.api.plugin.DiscoveryHandler;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginException; import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TorConstants; import org.briarproject.bramble.api.plugin.TorConstants;
@@ -56,9 +54,6 @@ import java.util.logging.Logger;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
@@ -70,11 +65,6 @@ import static java.util.logging.Logger.getLogger;
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY; import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.ID; import static org.briarproject.bramble.api.plugin.TorConstants.ID;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
@@ -86,9 +76,6 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHE
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V2; import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V2;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3; import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA;
import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES; import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES;
import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.copyAndClose;
import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.IoUtils.tryToClose;
@@ -126,14 +113,16 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final int maxLatency, maxIdleTime, socketTimeout; private final int maxLatency, maxIdleTime, socketTimeout;
private final File torDirectory, torFile, geoIpFile, obfs4File, configFile; private final File torDirectory, torFile, geoIpFile, obfs4File, configFile;
private final File doneFile, cookieFile; private final File doneFile, cookieFile;
private final ConnectionStatus connectionStatus;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
protected final PluginState state = new PluginState(); private volatile ServerSocket socket = null;
private volatile Socket controlSocket = null; private volatile Socket controlSocket = null;
private volatile TorControlConnection controlConnection = null; private volatile TorControlConnection controlConnection = null;
private volatile Settings settings = null; private volatile Settings settings = null;
protected volatile boolean running = false;
protected abstract int getProcessId(); protected abstract int getProcessId();
protected abstract long getLastUpdateTime(); protected abstract long getLastUpdateTime();
@@ -170,6 +159,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
configFile = new File(torDirectory, "torrc"); configFile = new File(torDirectory, "torrc");
doneFile = new File(torDirectory, "done"); doneFile = new File(torDirectory, "done");
cookieFile = new File(torDirectory, ".tor/control_auth_cookie"); cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
connectionStatus = new ConnectionStatus();
// 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("TorPlugin", ioExecutor, 1); new PoliteExecutor("TorPlugin", ioExecutor, 1);
@@ -200,11 +190,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
} }
// Load the settings // Load the settings
settings = migrateSettings(callback.getSettings()); settings = callback.getSettings();
// Install or update the assets if necessary // Install or update the assets if necessary
if (!assetsAreUpToDate()) installAssets(); if (!assetsAreUpToDate()) installAssets();
if (cookieFile.exists() && !cookieFile.delete()) if (cookieFile.exists() && !cookieFile.delete())
LOG.warning("Old auth cookie not deleted"); LOG.warning("Old auth cookie not deleted");
// Migrate old settings before having a chance to stop
migrateSettings();
// Start a new Tor process // Start a new Tor process
LOG.info("Starting Tor"); LOG.info("Starting Tor");
String torPath = torFile.getAbsolutePath(); String torPath = torFile.getAbsolutePath();
@@ -268,6 +260,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
// Tell Tor to exit when the control connection is closed // Tell Tor to exit when the control connection is closed
controlConnection.takeOwnership(); controlConnection.takeOwnership();
controlConnection.resetConf(singletonList(OWNER)); controlConnection.resetConf(singletonList(OWNER));
running = true;
// Register to receive events from the Tor process // Register to receive events from the Tor process
controlConnection.setEventHandler(this); controlConnection.setEventHandler(this);
controlConnection.setEvents(asList(EVENTS)); controlConnection.setEvents(asList(EVENTS));
@@ -275,12 +268,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
String phase = controlConnection.getInfo("status/bootstrap-phase"); String phase = controlConnection.getInfo("status/bootstrap-phase");
if (phase != null && phase.contains("PROGRESS=100")) { if (phase != null && phase.contains("PROGRESS=100")) {
LOG.info("Tor has already bootstrapped"); LOG.info("Tor has already bootstrapped");
state.setBootstrapped(); connectionStatus.setBootstrapped();
} }
} catch (IOException e) { } catch (IOException e) {
throw new PluginException(e); throw new PluginException(e);
} }
state.setStarted();
// Check whether we're online // Check whether we're online
updateConnectionStatus(networkManager.getNetworkStatus(), updateConnectionStatus(networkManager.getNetworkStatus(),
batteryManager.isCharging()); batteryManager.isCharging());
@@ -288,18 +280,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
bind(); bind();
} }
// TODO: Remove after a reasonable migration period (added 2020-01-16)
private Settings migrateSettings(Settings settings) {
int network = settings.getInt(PREF_TOR_NETWORK,
PREF_TOR_NETWORK_AUTOMATIC);
if (network == PREF_TOR_NETWORK_NEVER) {
settings.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_AUTOMATIC);
settings.putBoolean(PREF_PLUGIN_ENABLE, false);
callback.mergeSettings(settings);
}
return settings;
}
private boolean assetsAreUpToDate() { private boolean assetsAreUpToDate() {
return doneFile.lastModified() > getLastUpdateTime(); return doneFile.lastModified() > getLastUpdateTime();
} }
@@ -415,11 +395,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
tryToClose(ss, LOG, WARNING); tryToClose(ss, LOG, WARNING);
return; return;
} }
if (!state.setServerSocket(ss)) { if (!running) {
LOG.info("Closing redundant server socket");
tryToClose(ss, LOG, WARNING); tryToClose(ss, LOG, WARNING);
return; return;
} }
socket = ss;
// Store the port number // Store the port number
String localPort = String.valueOf(ss.getLocalPort()); String localPort = String.valueOf(ss.getLocalPort());
Settings s = new Settings(); Settings s = new Settings();
@@ -434,7 +414,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private void publishHiddenService(String port) { private void publishHiddenService(String port) {
if (!state.isTorRunning()) return; if (!running) return;
LOG.info("Creating hidden service"); LOG.info("Creating hidden service");
String privKey = settings.get(HS_PRIVKEY); String privKey = settings.get(HS_PRIVKEY);
Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port); Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port);
@@ -472,15 +452,14 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private void acceptContactConnections(ServerSocket ss) { private void acceptContactConnections(ServerSocket ss) {
while (true) { while (running) {
Socket s; Socket s;
try { try {
s = ss.accept(); s = ss.accept();
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
} catch (IOException e) { } catch (IOException e) {
// This is expected when the server socket is closed // This is expected when the socket is closed
LOG.info("Server socket closed"); if (LOG.isLoggable(INFO)) LOG.info(e.toString());
state.clearServerSocket(ss);
return; return;
} }
LOG.info("Connection received"); LOG.info("Connection received");
@@ -490,8 +469,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
protected void enableNetwork(boolean enable) throws IOException { protected void enableNetwork(boolean enable) throws IOException {
state.enableNetwork(enable); if (!running) return;
connectionStatus.enableNetwork(enable);
controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); controlConnection.setConf("DisableNetwork", enable ? "0" : "1");
if (!enable) callback.transportDisabled();
} }
private void enableBridges(boolean enable, boolean needsMeek) private void enableBridges(boolean enable, boolean needsMeek)
@@ -515,8 +496,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public void stop() { public void stop() {
ServerSocket ss = state.setStopped(); running = false;
tryToClose(ss, LOG, WARNING); tryToClose(socket, LOG, WARNING);
callback.transportDisabled();
if (controlSocket != null && controlConnection != null) { if (controlSocket != null && controlConnection != null) {
try { try {
LOG.info("Stopping Tor"); LOG.info("Stopping Tor");
@@ -530,13 +512,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public State getState() { public boolean isRunning() {
return state.getState(); return running && connectionStatus.isConnected();
}
@Override
public int getReasonsDisabled() {
return state.getReasonsDisabled();
} }
@Override @Override
@@ -550,9 +527,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public void poll( public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
List<Pair<TransportProperties, ConnectionHandler>> properties) { properties) {
if (getState() != ACTIVE) return; if (!isRunning()) return;
backoff.increment(); backoff.increment();
for (Pair<TransportProperties, ConnectionHandler> p : properties) { for (Pair<TransportProperties, ConnectionHandler> p : properties) {
connect(p.getFirst(), p.getSecond()); connect(p.getFirst(), p.getSecond());
@@ -571,7 +548,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public DuplexTransportConnection createConnection(TransportProperties p) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (getState() != ACTIVE) return null; if (!isRunning()) return null;
String bestOnion = null; String bestOnion = null;
String onion2 = p.get(PROP_ONION_V2); String onion2 = p.get(PROP_ONION_V2);
String onion3 = p.get(PROP_ONION_V3); String onion3 = p.get(PROP_ONION_V3);
@@ -659,8 +636,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
new TorTransportConnection(this, s)); new TorTransportConnection(this, s));
} }
} catch (IOException e) { } catch (IOException e) {
// This is expected when the server socket is closed // This is expected when the socket is closed
LOG.info("Rendezvous server socket closed"); if (LOG.isLoggable(INFO)) LOG.info(e.toString());
} }
}); });
Map<Integer, String> portLines = Map<Integer, String> portLines =
@@ -685,23 +662,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
} }
@Override
public boolean supportsDiscovery() {
return false;
}
@Override
public void discoverPeers(
List<Pair<TransportProperties, DiscoveryHandler>> properties) {
throw new UnsupportedOperationException();
}
@Override @Override
public void circuitStatus(String status, String id, String path) { public void circuitStatus(String status, String id, String path) {
if (status.equals("BUILT") && if (status.equals("BUILT") &&
state.getAndSetCircuitBuilt()) { connectionStatus.getAndSetCircuitBuilt()) {
LOG.info("First circuit built"); LOG.info("First circuit built");
backoff.reset(); backoff.reset();
if (isRunning()) callback.transportEnabled();
} }
} }
@@ -732,8 +699,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
public void message(String severity, String msg) { public void message(String severity, String msg) {
if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) {
state.setBootstrapped(); connectionStatus.setBootstrapped();
backoff.reset(); backoff.reset();
if (isRunning()) callback.transportEnabled();
} }
} }
@@ -770,7 +738,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private void disableNetwork() { private void disableNetwork() {
connectionStatusExecutor.execute(() -> { connectionStatusExecutor.execute(() -> {
try { try {
if (state.isTorRunning()) enableNetwork(false); enableNetwork(false);
} catch (IOException ex) { } catch (IOException ex) {
logException(LOG, WARNING, ex); logException(LOG, WARNING, ex);
} }
@@ -780,14 +748,12 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private void updateConnectionStatus(NetworkStatus status, private void updateConnectionStatus(NetworkStatus status,
boolean charging) { boolean charging) {
connectionStatusExecutor.execute(() -> { connectionStatusExecutor.execute(() -> {
if (!state.isTorRunning()) return; if (!running) return;
boolean online = status.isConnected(); boolean online = status.isConnected();
boolean wifi = status.isWifi(); boolean wifi = status.isWifi();
String country = locationUtils.getCurrentCountry(); String country = locationUtils.getCurrentCountry();
boolean blocked = boolean blocked =
circumventionProvider.isTorProbablyBlocked(country); circumventionProvider.isTorProbablyBlocked(country);
boolean enabledByUser =
settings.getBoolean(PREF_PLUGIN_ENABLE, true);
int network = settings.getInt(PREF_TOR_NETWORK, int network = settings.getInt(PREF_TOR_NETWORK,
PREF_TOR_NETWORK_AUTOMATIC); PREF_TOR_NETWORK_AUTOMATIC);
boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true); boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true);
@@ -798,70 +764,47 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Online: " + online + ", wifi: " + wifi); LOG.info("Online: " + online + ", wifi: " + wifi);
if (country.isEmpty()) LOG.info("Country code unknown"); if ("".equals(country)) LOG.info("Country code unknown");
else LOG.info("Country code: " + country); else LOG.info("Country code: " + country);
LOG.info("Charging: " + charging); LOG.info("Charging: " + charging);
} }
int reasonsDisabled = 0;
boolean enableNetwork = false, enableBridges = false;
boolean useMeek = false, enableConnectionPadding = false;
if (!online) {
LOG.info("Disabling network, device is offline");
} else {
if (!enabledByUser) {
LOG.info("User has disabled Tor");
reasonsDisabled |= REASON_USER;
}
if (!charging && onlyWhenCharging) {
LOG.info("Configured not to use battery");
reasonsDisabled |= REASON_BATTERY;
}
if (!useMobile && !wifi) {
LOG.info("Configured not to use mobile data");
reasonsDisabled |= REASON_MOBILE_DATA;
}
if (automatic && blocked && !bridgesWork) {
LOG.info("Country is blocked");
reasonsDisabled |= REASON_COUNTRY_BLOCKED;
}
if (reasonsDisabled != 0) {
LOG.info("Disabling network due to settings");
} else {
LOG.info("Enabling network");
enableNetwork = true;
if (network == PREF_TOR_NETWORK_WITH_BRIDGES ||
(automatic && bridgesWork)) {
if (circumventionProvider.needsMeek(country)) {
LOG.info("Using meek bridges");
enableBridges = true;
useMeek = true;
} else {
LOG.info("Using obfs4 bridges");
enableBridges = true;
}
} else {
LOG.info("Not using bridges");
}
if (wifi && charging) {
LOG.info("Enabling connection padding");
enableConnectionPadding = true;
} else {
LOG.info("Disabling connection padding");
}
}
}
state.setReasonsDisabled(reasonsDisabled);
try { try {
if (enableNetwork) { if (!online) {
enableBridges(enableBridges, useMeek); LOG.info("Disabling network, device is offline");
enableConnectionPadding(enableConnectionPadding); enableNetwork(false);
} else if (!charging && onlyWhenCharging) {
LOG.info("Disabling network, device is on battery");
enableNetwork(false);
} else if (network == PREF_TOR_NETWORK_NEVER ||
(!useMobile && !wifi)) {
LOG.info("Disabling network, device is using mobile data");
enableNetwork(false);
} else if (automatic && blocked && !bridgesWork) {
LOG.info("Disabling network, country is blocked");
enableNetwork(false);
} else if (network == PREF_TOR_NETWORK_WITH_BRIDGES ||
(automatic && bridgesWork)) {
if (circumventionProvider.needsMeek(country)) {
LOG.info("Enabling network, using meek bridges");
enableBridges(true, true);
} else {
LOG.info("Enabling network, using obfs4 bridges");
enableBridges(true, false);
}
enableNetwork(true);
} else {
LOG.info("Enabling network, not using bridges");
enableBridges(false, false);
enableNetwork(true);
}
if (online && wifi && charging) {
LOG.info("Enabling connection padding");
enableConnectionPadding(true);
} else {
LOG.info("Disabling connection padding");
enableConnectionPadding(false);
} }
enableNetwork(enableNetwork);
} catch (IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
} }
@@ -869,96 +812,48 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private void enableConnectionPadding(boolean enable) throws IOException { private void enableConnectionPadding(boolean enable) throws IOException {
if (!running) return;
controlConnection.setConf("ConnectionPadding", enable ? "1" : "0"); controlConnection.setConf("ConnectionPadding", enable ? "1" : "0");
} }
@ThreadSafe // TODO remove when sufficient time has passed. Added 2018-08-15
@NotNullByDefault private void migrateSettings() {
protected class PluginState { Settings sOld = callback.getSettings();
int oldNetwork = sOld.getInt("network", -1);
@GuardedBy("this") if (oldNetwork == -1) return;
private boolean started = false, Settings s = new Settings();
stopped = false, if (oldNetwork == 0) {
networkInitialised = false, s.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_NEVER);
networkEnabled = false, } else if (oldNetwork == 1) {
bootstrapped = false, s.putBoolean(PREF_TOR_MOBILE, false);
circuitBuilt = false,
settingsChecked = false;
@GuardedBy("this")
private int reasonsDisabled = 0;
@GuardedBy("this")
@Nullable
private ServerSocket serverSocket = null;
synchronized void setStarted() {
started = true;
callback.pluginStateChanged(getState());
} }
s.putInt("network", -1);
callback.mergeSettings(s);
}
synchronized boolean isTorRunning() { private static class ConnectionStatus {
return started && !stopped;
}
@Nullable // All of the following are locking: this
synchronized ServerSocket setStopped() { private boolean networkEnabled = false;
stopped = true; private boolean bootstrapped = false, circuitBuilt = false;
ServerSocket ss = serverSocket;
serverSocket = null;
callback.pluginStateChanged(getState());
return ss;
}
synchronized void setBootstrapped() { private synchronized void setBootstrapped() {
bootstrapped = true; bootstrapped = true;
callback.pluginStateChanged(getState());
} }
synchronized boolean getAndSetCircuitBuilt() { private synchronized boolean getAndSetCircuitBuilt() {
boolean firstCircuit = !circuitBuilt; boolean firstCircuit = !circuitBuilt;
circuitBuilt = true; circuitBuilt = true;
callback.pluginStateChanged(getState());
return firstCircuit; return firstCircuit;
} }
synchronized void enableNetwork(boolean enable) { private synchronized void enableNetwork(boolean enable) {
networkInitialised = true;
networkEnabled = enable; networkEnabled = enable;
if (!enable) circuitBuilt = false; if (!enable) circuitBuilt = false;
callback.pluginStateChanged(getState());
} }
synchronized void setReasonsDisabled(int reasonsDisabled) { private synchronized boolean isConnected() {
settingsChecked = true; return networkEnabled && bootstrapped && circuitBuilt;
this.reasonsDisabled = reasonsDisabled;
callback.pluginStateChanged(getState());
}
// Doesn't affect getState()
synchronized boolean setServerSocket(ServerSocket ss) {
if (stopped || serverSocket != null) return false;
serverSocket = ss;
return true;
}
// Doesn't affect getState()
synchronized void clearServerSocket(ServerSocket ss) {
if (serverSocket == ss) serverSocket = null;
}
synchronized State getState() {
if (!started || stopped || !settingsChecked) {
return STARTING_STOPPING;
}
if (reasonsDisabled != 0) return DISABLED;
if (!networkInitialised) return ENABLING;
if (!networkEnabled) return INACTIVE;
return bootstrapped && circuitBuilt ? ACTIVE : ENABLING;
}
synchronized int getReasonsDisabled() {
return getState() == DISABLED ? reasonsDisabled : 0;
} }
} }
} }

View File

@@ -284,7 +284,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
meta.put("transportId", t.getString()); meta.put("transportId", t.getString());
meta.put("version", version); meta.put("version", version);
meta.put("local", local); meta.put("local", local);
clientHelper.addLocalMessage(txn, m, meta, shared, false); clientHelper.addLocalMessage(txn, m, meta, shared);
} catch (FormatException e) { } catch (FormatException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -31,8 +31,8 @@ import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
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;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
@@ -66,7 +66,6 @@ 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.api.contact.PendingContactState.ADDING_CONTACT; import static org.briarproject.bramble.api.contact.PendingContactState.ADDING_CONTACT;
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED; import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
import static org.briarproject.bramble.api.contact.PendingContactState.OFFLINE;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION; import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNull; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNull;
import static org.briarproject.bramble.rendezvous.RendezvousConstants.POLLING_INTERVAL_MS; import static org.briarproject.bramble.rendezvous.RendezvousConstants.POLLING_INTERVAL_MS;
@@ -159,7 +158,9 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
private void addPendingContact(PendingContact p) { private void addPendingContact(PendingContact p) {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
long expiry = p.getTimestamp() + RENDEZVOUS_TIMEOUT_MS; long expiry = p.getTimestamp() + RENDEZVOUS_TIMEOUT_MS;
if (expiry <= now) { if (expiry > now) {
broadcastState(p.getId(), WAITING_FOR_CONNECTION);
} else {
broadcastState(p.getId(), FAILED); broadcastState(p.getId(), FAILED);
return; return;
} }
@@ -179,13 +180,9 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
for (PluginState ps : pluginStates.values()) { for (PluginState ps : pluginStates.values()) {
RendezvousEndpoint endpoint = RendezvousEndpoint endpoint =
createEndpoint(ps.plugin, p.getId(), cs); createEndpoint(ps.plugin, p.getId(), cs);
if (endpoint != null) { if (endpoint != null)
requireNull(ps.endpoints.put(p.getId(), endpoint)); requireNull(ps.endpoints.put(p.getId(), endpoint));
cs.numEndpoints++;
}
} }
if (cs.numEndpoints == 0) broadcastState(p.getId(), OFFLINE);
else broadcastState(p.getId(), WAITING_FOR_CONNECTION);
} catch (DbException | GeneralSecurityException e) { } catch (DbException | GeneralSecurityException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
} }
@@ -269,11 +266,11 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
} else if (e instanceof PendingContactRemovedEvent) { } else if (e instanceof PendingContactRemovedEvent) {
PendingContactRemovedEvent p = (PendingContactRemovedEvent) e; PendingContactRemovedEvent p = (PendingContactRemovedEvent) e;
removePendingContactAsync(p.getId()); removePendingContactAsync(p.getId());
} else if (e instanceof TransportActiveEvent) { } else if (e instanceof TransportEnabledEvent) {
TransportActiveEvent t = (TransportActiveEvent) e; TransportEnabledEvent t = (TransportEnabledEvent) e;
addTransportAsync(t.getTransportId()); addTransportAsync(t.getTransportId());
} else if (e instanceof TransportInactiveEvent) { } else if (e instanceof TransportDisabledEvent) {
TransportInactiveEvent t = (TransportInactiveEvent) e; TransportDisabledEvent t = (TransportDisabledEvent) e;
removeTransportAsync(t.getTransportId()); removeTransportAsync(t.getTransportId());
} else if (e instanceof RendezvousConnectionOpenedEvent) { } else if (e instanceof RendezvousConnectionOpenedEvent) {
RendezvousConnectionOpenedEvent r = RendezvousConnectionOpenedEvent r =
@@ -331,14 +328,9 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
TransportId t = plugin.getId(); TransportId t = plugin.getId();
Map<PendingContactId, RendezvousEndpoint> endpoints = new HashMap<>(); Map<PendingContactId, RendezvousEndpoint> endpoints = new HashMap<>();
for (Entry<PendingContactId, CryptoState> e : cryptoStates.entrySet()) { for (Entry<PendingContactId, CryptoState> e : cryptoStates.entrySet()) {
PendingContactId p = e.getKey(); RendezvousEndpoint endpoint =
CryptoState cs = e.getValue(); createEndpoint(plugin, e.getKey(), e.getValue());
RendezvousEndpoint endpoint = createEndpoint(plugin, p, cs); if (endpoint != null) endpoints.put(e.getKey(), endpoint);
if (endpoint != null) {
endpoints.put(p, endpoint);
if (++cs.numEndpoints == 1)
broadcastState(p, WAITING_FOR_CONNECTION);
}
} }
requireNull(pluginStates.put(t, new PluginState(plugin, endpoints))); requireNull(pluginStates.put(t, new PluginState(plugin, endpoints)));
} }
@@ -352,11 +344,8 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
private void removeTransport(TransportId t) { private void removeTransport(TransportId t) {
PluginState ps = pluginStates.remove(t); PluginState ps = pluginStates.remove(t);
if (ps != null) { if (ps != null) {
for (Entry<PendingContactId, RendezvousEndpoint> e : for (RendezvousEndpoint endpoint : ps.endpoints.values()) {
ps.endpoints.entrySet()) { tryToClose(endpoint, LOG, INFO);
tryToClose(e.getValue(), LOG, INFO);
CryptoState cs = cryptoStates.get(e.getKey());
if (--cs.numEndpoints == 0) broadcastState(e.getKey(), OFFLINE);
} }
} }
} }
@@ -402,8 +391,6 @@ class RendezvousPollerImpl implements RendezvousPoller, Service, EventListener {
private final boolean alice; private final boolean alice;
private final long expiry; private final long expiry;
private int numEndpoints = 0;
private CryptoState(SecretKey rendezvousKey, boolean alice, private CryptoState(SecretKey rendezvousKey, boolean alice,
long expiry) { long expiry) {
this.rendezvousKey = rendezvousKey; this.rendezvousKey = rendezvousKey;

View File

@@ -6,7 +6,7 @@ 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.TorConstants; import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.reporting.DevConfig; import org.briarproject.bramble.api.reporting.DevConfig;
import org.briarproject.bramble.api.reporting.DevReporter; import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
@@ -92,8 +92,8 @@ class DevReporterImpl implements DevReporter, EventListener {
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof TransportActiveEvent) { if (e instanceof TransportEnabledEvent) {
TransportActiveEvent t = (TransportActiveEvent) e; TransportEnabledEvent t = (TransportEnabledEvent) e;
if (t.getTransportId().equals(TorConstants.ID)) if (t.getTransportId().equals(TorConstants.ID))
ioExecutor.execute(this::sendReports); ioExecutor.execute(this::sendReports);
} }

View File

@@ -17,7 +17,6 @@ import org.briarproject.bramble.api.sync.Offer;
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;
import org.briarproject.bramble.api.sync.Versions;
import org.briarproject.bramble.api.sync.event.GroupVisibilityUpdatedEvent; import org.briarproject.bramble.api.sync.event.GroupVisibilityUpdatedEvent;
import org.briarproject.bramble.api.sync.event.MessageRequestedEvent; import org.briarproject.bramble.api.sync.event.MessageRequestedEvent;
import org.briarproject.bramble.api.sync.event.MessageSharedEvent; import org.briarproject.bramble.api.sync.event.MessageSharedEvent;
@@ -40,11 +39,9 @@ import javax.annotation.concurrent.ThreadSafe;
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;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING;
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.SyncConstants.MAX_MESSAGE_IDS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
import static org.briarproject.bramble.api.sync.SyncConstants.SUPPORTED_VERSIONS;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
/** /**
@@ -58,7 +55,7 @@ import static org.briarproject.bramble.util.LogUtils.logException;
class DuplexOutgoingSession implements SyncSession, EventListener { class DuplexOutgoingSession implements SyncSession, EventListener {
private static final Logger LOG = private static final Logger LOG =
getLogger(DuplexOutgoingSession.class.getName()); Logger.getLogger(DuplexOutgoingSession.class.getName());
private static final ThrowingRunnable<IOException> CLOSE = () -> { private static final ThrowingRunnable<IOException> CLOSE = () -> {
}; };
@@ -106,8 +103,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
public void run() throws IOException { public void run() throws IOException {
eventBus.addListener(this); eventBus.addListener(this);
try { try {
// Send our supported protocol versions
recordWriter.writeVersions(new Versions(SUPPORTED_VERSIONS));
// Start a query for each type of record // Start a query for each type of record
generateAck(); generateAck();
generateBatch(); generateBatch();

View File

@@ -18,17 +18,14 @@ import org.briarproject.bramble.api.sync.Offer;
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;
import org.briarproject.bramble.api.sync.Versions;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
@@ -40,7 +37,7 @@ import static org.briarproject.bramble.util.LogUtils.logException;
class IncomingSession implements SyncSession, EventListener { class IncomingSession implements SyncSession, EventListener {
private static final Logger LOG = private static final Logger LOG =
getLogger(IncomingSession.class.getName()); Logger.getLogger(IncomingSession.class.getName());
private final DatabaseComponent db; private final DatabaseComponent db;
private final Executor dbExecutor; private final Executor dbExecutor;
@@ -83,9 +80,6 @@ class IncomingSession implements SyncSession, EventListener {
} else if (recordReader.hasRequest()) { } else if (recordReader.hasRequest()) {
Request r = recordReader.readRequest(); Request r = recordReader.readRequest();
dbExecutor.execute(new ReceiveRequest(r)); dbExecutor.execute(new ReceiveRequest(r));
} else if (recordReader.hasVersions()) {
Versions v = recordReader.readVersions();
dbExecutor.execute(new ReceiveVersions(v));
} else { } else {
// unknown records are ignored in RecordReader#eof() // unknown records are ignored in RecordReader#eof()
throw new FormatException(); throw new FormatException();
@@ -196,26 +190,4 @@ class IncomingSession implements SyncSession, EventListener {
} }
} }
} }
private class ReceiveVersions implements Runnable {
private final Versions versions;
private ReceiveVersions(Versions versions) {
this.versions = versions;
}
@DatabaseExecutor
@Override
public void run() {
try {
List<Byte> supported = versions.getSupportedVersions();
db.transaction(false,
txn -> db.setSyncVersions(txn, contactId, supported));
} catch (DbException e) {
logException(LOG, WARNING, e);
interrupt();
}
}
}
} }

View File

@@ -15,7 +15,6 @@ 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.SyncRecordWriter; import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.SyncSession; import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.Versions;
import org.briarproject.bramble.api.transport.StreamWriter; import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.IOException; import java.io.IOException;
@@ -30,11 +29,9 @@ import javax.annotation.concurrent.ThreadSafe;
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;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING;
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.SyncConstants.MAX_MESSAGE_IDS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
import static org.briarproject.bramble.api.sync.SyncConstants.SUPPORTED_VERSIONS;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
/** /**
@@ -47,7 +44,7 @@ import static org.briarproject.bramble.util.LogUtils.logException;
class SimplexOutgoingSession implements SyncSession, EventListener { class SimplexOutgoingSession implements SyncSession, EventListener {
private static final Logger LOG = private static final Logger LOG =
getLogger(SimplexOutgoingSession.class.getName()); Logger.getLogger(SimplexOutgoingSession.class.getName());
private static final ThrowingRunnable<IOException> CLOSE = () -> { private static final ThrowingRunnable<IOException> CLOSE = () -> {
}; };
@@ -83,8 +80,6 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
public void run() throws IOException { public void run() throws IOException {
eventBus.addListener(this); eventBus.addListener(this);
try { try {
// Send our supported protocol versions
recordWriter.writeVersions(new Versions(SUPPORTED_VERSIONS));
// Start a query for each type of record // Start a query for each type of record
dbExecutor.execute(new GenerateAck()); dbExecutor.execute(new GenerateAck());
dbExecutor.execute(new GenerateBatch()); dbExecutor.execute(new GenerateBatch());

View File

@@ -13,7 +13,6 @@ 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.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.util.ByteUtils; import org.briarproject.bramble.util.ByteUtils;
import java.io.IOException; import java.io.IOException;
@@ -27,8 +26,6 @@ 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.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.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.PROTOCOL_VERSION; import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
@@ -48,7 +45,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 == REQUEST;
} }
private final MessageFactory messageFactory; private final MessageFactory messageFactory;
@@ -151,27 +148,4 @@ class SyncRecordReaderImpl implements SyncRecordReader {
if (!hasRequest()) throw new FormatException(); if (!hasRequest()) throw new FormatException();
return new Request(readMessageIds()); return new Request(readMessageIds());
} }
@Override
public boolean hasVersions() throws IOException {
return !eof() && getNextRecordType() == VERSIONS;
}
@Override
public Versions readVersions() throws IOException {
if (!hasVersions()) throw new FormatException();
return new Versions(readSupportedVersions());
}
private List<Byte> readSupportedVersions() throws IOException {
if (nextRecord == null) throw new AssertionError();
byte[] payload = nextRecord.getPayload();
if (payload.length == 0) throw new FormatException();
if (payload.length > MAX_SUPPORTED_VERSIONS)
throw new FormatException();
List<Byte> supported = new ArrayList<>(payload.length);
for (byte b : payload) supported.add(b);
nextRecord = null;
return supported;
}
} }

View File

@@ -10,7 +10,6 @@ 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.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 java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@@ -21,7 +20,6 @@ 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.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.SyncConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
@NotThreadSafe @NotThreadSafe
@@ -67,12 +65,6 @@ class SyncRecordWriterImpl implements SyncRecordWriter {
writeRecord(REQUEST); writeRecord(REQUEST);
} }
@Override
public void writeVersions(Versions v) throws IOException {
for (byte b : v.getSupportedVersions()) payload.write(b);
writeRecord(VERSIONS);
}
@Override @Override
public void flush() throws IOException { public void flush() throws IOException {
writer.flush(); writer.flush();

View File

@@ -314,7 +314,6 @@ class ValidationManagerImpl implements ValidationManager, Service,
try { try {
shareMsg = hook.incomingMessage(txn, m, meta); shareMsg = hook.incomingMessage(txn, m, meta);
} catch (InvalidMessageException e) { } catch (InvalidMessageException e) {
logException(LOG, INFO, e);
invalidateMessage(txn, m.getId()); invalidateMessage(txn, m.getId());
return new DeliveryResult(false, false); return new DeliveryResult(false, false);
} }

View File

@@ -26,9 +26,7 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook; import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientMajorVersion; import org.briarproject.bramble.api.versioning.ClientMajorVersion;
import org.briarproject.bramble.api.versioning.ClientVersion;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.event.ClientVersionUpdatedEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -82,9 +80,9 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
@Override @Override
public void registerClient(ClientId clientId, int majorVersion, public void registerClient(ClientId clientId, int majorVersion,
int minorVersion, ClientVersioningHook hook) { int minorVersion, ClientVersioningHook hook) {
ClientMajorVersion cmv = new ClientMajorVersion(clientId, majorVersion); ClientMajorVersion cv = new ClientMajorVersion(clientId, majorVersion);
clients.add(new ClientVersion(cmv, minorVersion)); clients.add(new ClientVersion(cv, minorVersion));
hooks.put(cmv, hook); hooks.put(cv, hook);
} }
@Override @Override
@@ -98,9 +96,9 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
Update remoteUpdate = loadUpdate(txn, latest.remote.messageId); Update remoteUpdate = loadUpdate(txn, latest.remote.messageId);
Map<ClientMajorVersion, Visibility> visibilities = Map<ClientMajorVersion, Visibility> visibilities =
getVisibilities(localUpdate.states, remoteUpdate.states); getVisibilities(localUpdate.states, remoteUpdate.states);
ClientMajorVersion key = ClientMajorVersion cv =
new ClientMajorVersion(clientId, majorVersion); new ClientMajorVersion(clientId, majorVersion);
Visibility v = visibilities.get(key); Visibility v = visibilities.get(cv);
return v == null ? INVISIBLE : v; return v == null ? INVISIBLE : v;
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
@@ -114,11 +112,10 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
LatestUpdates latest = findLatestUpdates(txn, contactId); LatestUpdates latest = findLatestUpdates(txn, contactId);
if (latest == null || latest.remote == null) return -1; if (latest == null || latest.remote == null) return -1;
Update remoteUpdate = loadUpdate(txn, latest.remote.messageId); Update remoteUpdate = loadUpdate(txn, latest.remote.messageId);
ClientMajorVersion key = ClientMajorVersion cv =
new ClientMajorVersion(clientId, majorVersion); new ClientMajorVersion(clientId, majorVersion);
for (ClientState remote : remoteUpdate.states) { for (ClientState remote : remoteUpdate.states) {
if (remote.clientVersion.getClientMajorVersion().equals(key)) if (remote.majorVersion.equals(cv)) return remote.minorVersion;
return remote.clientVersion.getMinorVersion();
} }
return -1; return -1;
} catch (FormatException e) { } catch (FormatException e) {
@@ -229,21 +226,9 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
Map<ClientMajorVersion, Visibility> after = Map<ClientMajorVersion, Visibility> after =
getVisibilities(newLocalStates, newRemoteStates); getVisibilities(newLocalStates, newRemoteStates);
// Call hooks for any visibilities that have changed // Call hooks for any visibilities that have changed
ContactId c = getContactId(txn, m.getGroupId());
if (!before.equals(after)) { if (!before.equals(after)) {
Contact contact = db.getContact(txn, c); Contact c = getContact(txn, m.getGroupId());
callVisibilityHooks(txn, contact, before, after); callVisibilityHooks(txn, c, before, after);
}
// Broadcast events for any new client versions
Set<ClientVersion> oldRemoteVersions = new HashSet<>();
for (ClientState cs : oldRemoteStates) {
oldRemoteVersions.add(cs.clientVersion);
}
for (ClientState cs : newRemoteStates) {
if (!oldRemoteVersions.contains(cs.clientVersion)) {
txn.attach(new ClientVersionUpdatedEvent(c,
cs.clientVersion));
}
} }
} catch (FormatException e) { } catch (FormatException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
@@ -258,7 +243,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
try { try {
Message m = clientHelper.createMessage(localGroup.getId(), now, Message m = clientHelper.createMessage(localGroup.getId(), now,
body); body);
db.addLocalMessage(txn, m, new Metadata(), false, false); db.addLocalMessage(txn, m, new Metadata(), false);
} catch (FormatException e) { } catch (FormatException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
@@ -271,8 +256,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} }
private BdfList encodeClientVersion(ClientVersion cv) { private BdfList encodeClientVersion(ClientVersion cv) {
return BdfList.of(cv.getClientId().getString(), cv.getMajorVersion(), return BdfList.of(cv.majorVersion.getClientId().getString(),
cv.getMinorVersion()); cv.majorVersion.getMajorVersion(), cv.minorVersion);
} }
/** /**
@@ -313,7 +298,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
ClientId clientId = new ClientId(cv.getString(0)); ClientId clientId = new ClientId(cv.getString(0));
int majorVersion = cv.getLong(1).intValue(); int majorVersion = cv.getLong(1).intValue();
int minorVersion = cv.getLong(2).intValue(); int minorVersion = cv.getLong(2).intValue();
parsed.add(new ClientVersion(clientId, majorVersion, minorVersion)); parsed.add(new ClientVersion(clientId, majorVersion,
minorVersion));
} }
return parsed; return parsed;
} }
@@ -432,15 +418,13 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
private List<ClientState> updateStatesFromLocalVersions( private List<ClientState> updateStatesFromLocalVersions(
List<ClientState> oldStates, List<ClientVersion> newVersions) { List<ClientState> oldStates, List<ClientVersion> newVersions) {
Map<ClientMajorVersion, ClientState> oldMap = new HashMap<>(); Map<ClientMajorVersion, ClientState> oldMap = new HashMap<>();
for (ClientState cs : oldStates) { for (ClientState cs : oldStates) oldMap.put(cs.majorVersion, cs);
oldMap.put(cs.clientVersion.getClientMajorVersion(), cs);
}
List<ClientState> newStates = new ArrayList<>(newVersions.size()); List<ClientState> newStates = new ArrayList<>(newVersions.size());
for (ClientVersion newVersion : newVersions) { for (ClientVersion newVersion : newVersions) {
ClientMajorVersion key = newVersion.getClientMajorVersion(); ClientState oldState = oldMap.get(newVersion.majorVersion);
ClientState oldState = oldMap.get(key);
boolean active = oldState != null && oldState.active; boolean active = oldState != null && oldState.active;
newStates.add(new ClientState(newVersion, active)); newStates.add(new ClientState(newVersion.majorVersion,
newVersion.minorVersion, active));
} }
return newStates; return newStates;
} }
@@ -454,7 +438,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_UPDATE_VERSION, updateVersion); meta.put(MSG_KEY_UPDATE_VERSION, updateVersion);
meta.put(MSG_KEY_LOCAL, true); meta.put(MSG_KEY_LOCAL, true);
clientHelper.addLocalMessage(txn, m, meta, true, false); clientHelper.addLocalMessage(txn, m, meta, true);
} catch (FormatException e) { } catch (FormatException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -467,25 +451,21 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} }
private BdfList encodeClientState(ClientState cs) { private BdfList encodeClientState(ClientState cs) {
ClientVersion cv = cs.clientVersion; return BdfList.of(cs.majorVersion.getClientId().getString(),
return BdfList.of(cv.getClientId().getString(), cv.getMajorVersion(), cs.majorVersion.getMajorVersion(), cs.minorVersion, cs.active);
cv.getMinorVersion(), cs.active);
} }
private Map<ClientMajorVersion, Visibility> getVisibilities( private Map<ClientMajorVersion, Visibility> getVisibilities(
List<ClientState> localStates, List<ClientState> remoteStates) { List<ClientState> localStates, List<ClientState> remoteStates) {
Map<ClientMajorVersion, ClientState> remoteMap = new HashMap<>(); Map<ClientMajorVersion, ClientState> remoteMap = new HashMap<>();
for (ClientState cs : remoteStates) { for (ClientState cs : remoteStates) remoteMap.put(cs.majorVersion, cs);
remoteMap.put(cs.clientVersion.getClientMajorVersion(), cs);
}
Map<ClientMajorVersion, Visibility> visibilities = new HashMap<>(); Map<ClientMajorVersion, Visibility> visibilities = new HashMap<>();
for (ClientState local : localStates) { for (ClientState local : localStates) {
ClientMajorVersion key = ClientState remote = remoteMap.get(local.majorVersion);
local.clientVersion.getClientMajorVersion(); if (remote == null) visibilities.put(local.majorVersion, INVISIBLE);
ClientState remote = remoteMap.get(key); else if (remote.active)
if (remote == null) visibilities.put(key, INVISIBLE); visibilities.put(local.majorVersion, SHARED);
else if (remote.active) visibilities.put(key, SHARED); else visibilities.put(local.majorVersion, VISIBLE);
else visibilities.put(key, VISIBLE);
} }
return visibilities; return visibilities;
} }
@@ -506,9 +486,9 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} }
} }
private void callVisibilityHook(Transaction txn, ClientMajorVersion cmv, private void callVisibilityHook(Transaction txn, ClientMajorVersion cv,
Contact c, Visibility v) throws DbException { Contact c, Visibility v) throws DbException {
ClientVersioningHook hook = hooks.get(cmv); ClientVersioningHook hook = hooks.get(cv);
if (hook != null) hook.onClientVisibilityChanging(txn, c, v); if (hook != null) hook.onClientVisibilityChanging(txn, c, v);
} }
@@ -516,17 +496,18 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
List<ClientVersion> versions) throws DbException { List<ClientVersion> versions) throws DbException {
List<ClientState> states = new ArrayList<>(versions.size()); List<ClientState> states = new ArrayList<>(versions.size());
for (ClientVersion cv : versions) { for (ClientVersion cv : versions) {
states.add(new ClientState(cv, false)); states.add(new ClientState(cv.majorVersion, cv.minorVersion,
false));
} }
storeUpdate(txn, g, states, 1); storeUpdate(txn, g, states, 1);
} }
private ContactId getContactId(Transaction txn, GroupId g) private Contact getContact(Transaction txn, GroupId g) throws DbException {
throws DbException {
try { try {
BdfDictionary meta = BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g); clientHelper.getGroupMetadataAsDictionary(txn, g);
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue()); int id = meta.getLong(GROUP_KEY_CONTACT_ID).intValue();
return db.getContact(txn, new ContactId(id));
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }
@@ -535,16 +516,13 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
private List<ClientState> updateStatesFromRemoteStates( private List<ClientState> updateStatesFromRemoteStates(
List<ClientState> oldLocalStates, List<ClientState> remoteStates) { List<ClientState> oldLocalStates, List<ClientState> remoteStates) {
Set<ClientMajorVersion> remoteSet = new HashSet<>(); Set<ClientMajorVersion> remoteSet = new HashSet<>();
for (ClientState cs : remoteStates) { for (ClientState cs : remoteStates) remoteSet.add(cs.majorVersion);
remoteSet.add(cs.clientVersion.getClientMajorVersion());
}
List<ClientState> newLocalStates = List<ClientState> newLocalStates =
new ArrayList<>(oldLocalStates.size()); new ArrayList<>(oldLocalStates.size());
for (ClientState oldState : oldLocalStates) { for (ClientState oldState : oldLocalStates) {
ClientMajorVersion cmv = boolean active = remoteSet.contains(oldState.majorVersion);
oldState.clientVersion.getClientMajorVersion(); newLocalStates.add(new ClientState(oldState.majorVersion,
boolean active = remoteSet.contains(cmv); oldState.minorVersion, active));
newLocalStates.add(new ClientState(oldState.clientVersion, active));
} }
return newLocalStates; return newLocalStates;
} }
@@ -583,19 +561,61 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} }
} }
private static class ClientVersion implements Comparable<ClientVersion> {
private final ClientMajorVersion majorVersion;
private final int minorVersion;
private ClientVersion(ClientMajorVersion majorVersion,
int minorVersion) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
}
private ClientVersion(ClientId clientId, int majorVersion,
int minorVersion) {
this(new ClientMajorVersion(clientId, majorVersion), minorVersion);
}
@Override
public boolean equals(Object o) {
if (o instanceof ClientVersion) {
ClientVersion cv = (ClientVersion) o;
return majorVersion.equals(cv.majorVersion)
&& minorVersion == cv.minorVersion;
}
return false;
}
@Override
public int hashCode() {
return majorVersion.hashCode();
}
@Override
public int compareTo(ClientVersion cv) {
int compare = majorVersion.compareTo(cv.majorVersion);
if (compare != 0) return compare;
return minorVersion - cv.minorVersion;
}
}
private static class ClientState { private static class ClientState {
private final ClientVersion clientVersion; private final ClientMajorVersion majorVersion;
private final int minorVersion;
private final boolean active; private final boolean active;
private ClientState(ClientVersion clientVersion, boolean active) { private ClientState(ClientMajorVersion majorVersion, int minorVersion,
this.clientVersion = clientVersion; boolean active) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.active = active; this.active = active;
} }
private ClientState(ClientId clientId, int majorVersion, private ClientState(ClientId clientId, int majorVersion,
int minorVersion, boolean active) { int minorVersion, boolean active) {
this(new ClientVersion(clientId, majorVersion, minorVersion), this(new ClientMajorVersion(clientId, majorVersion), minorVersion,
active); active);
} }
@@ -603,7 +623,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
public boolean equals(Object o) { public boolean equals(Object o) {
if (o instanceof ClientState) { if (o instanceof ClientState) {
ClientState cs = (ClientState) o; ClientState cs = (ClientState) o;
return clientVersion.equals(cs.clientVersion) return majorVersion.equals(cs.majorVersion)
&& minorVersion == cs.minorVersion
&& active == cs.active; && active == cs.active;
} }
return false; return false;
@@ -611,7 +632,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
@Override @Override
public int hashCode() { public int hashCode() {
return clientVersion.hashCode(); return majorVersion.hashCode();
} }
} }
} }

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.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;
import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.identity.Identity;
@@ -40,8 +39,6 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
private final DatabaseConfig databaseConfig = private final DatabaseConfig databaseConfig =
context.mock(DatabaseConfig.class); context.mock(DatabaseConfig.class);
private final KeyStrengthener keyStrengthener =
context.mock(KeyStrengthener.class);
private final CryptoComponent crypto = context.mock(CryptoComponent.class); private final CryptoComponent crypto = context.mock(CryptoComponent.class);
private final IdentityManager identityManager = private final IdentityManager identityManager =
context.mock(IdentityManager.class); context.mock(IdentityManager.class);
@@ -71,8 +68,6 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
will(returnValue(dbDir)); will(returnValue(dbDir));
allowing(databaseConfig).getDatabaseKeyDirectory(); allowing(databaseConfig).getDatabaseKeyDirectory();
will(returnValue(keyDir)); will(returnValue(keyDir));
allowing(databaseConfig).getKeyStrengthener();
will(returnValue(keyStrengthener));
}}); }});
accountManager = accountManager =
@@ -94,8 +89,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testSignInReturnsFalseIfPasswordIsWrong() 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);
will(returnValue(null)); will(returnValue(null));
}}); }});
@@ -115,11 +109,8 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testSignInReturnsTrueIfPasswordIsRight() throws Exception { public void testSignInReturnsTrueIfPasswordIsRight() throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).decryptWithPassword(encryptedKey, password, oneOf(crypto).decryptWithPassword(encryptedKey, password);
keyStrengthener);
will(returnValue(key.getBytes())); will(returnValue(key.getBytes()));
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey);
will(returnValue(true));
}}); }});
storeDatabaseKey(keyFile, encryptedKeyHex); storeDatabaseKey(keyFile, encryptedKeyHex);
@@ -138,35 +129,6 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
} }
@Test
public void testSignInReEncryptsKey() throws Exception {
context.checking(new Expectations() {{
oneOf(crypto).decryptWithPassword(encryptedKey, password,
keyStrengthener);
will(returnValue(key.getBytes()));
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey);
will(returnValue(false));
oneOf(crypto).encryptWithPassword(key.getBytes(), password,
keyStrengthener);
will(returnValue(newEncryptedKey));
}});
storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
assertTrue(accountManager.signIn(password));
assertTrue(accountManager.hasDatabaseKey());
SecretKey decrypted = accountManager.getDatabaseKey();
assertNotNull(decrypted);
assertArrayEquals(key.getBytes(), decrypted.getBytes());
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
}
@Test @Test
public void testDbKeyIsLoadedFromPrimaryFile() throws Exception { public void testDbKeyIsLoadedFromPrimaryFile() throws Exception {
storeDatabaseKey(keyFile, encryptedKeyHex); storeDatabaseKey(keyFile, encryptedKeyHex);
@@ -296,8 +258,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
oneOf(identityManager).registerIdentity(identity); oneOf(identityManager).registerIdentity(identity);
oneOf(crypto).generateSecretKey(); oneOf(crypto).generateSecretKey();
will(returnValue(key)); will(returnValue(key));
oneOf(crypto).encryptWithPassword(key.getBytes(), password, oneOf(crypto).encryptWithPassword(key.getBytes(), password);
keyStrengthener);
will(returnValue(encryptedKey)); will(returnValue(encryptedKey));
}}); }});
@@ -326,8 +287,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
public void testChangePasswordReturnsFalseIfPasswordIsWrong() 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);
will(returnValue(null)); will(returnValue(null));
}}); }});
@@ -344,13 +304,9 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
public void testChangePasswordReturnsTrueIfPasswordIsRight() public void testChangePasswordReturnsTrueIfPasswordIsRight()
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).decryptWithPassword(encryptedKey, password, oneOf(crypto).decryptWithPassword(encryptedKey, password);
keyStrengthener);
will(returnValue(key.getBytes())); will(returnValue(key.getBytes()));
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey); oneOf(crypto).encryptWithPassword(key.getBytes(), newPassword);
will(returnValue(true));
oneOf(crypto).encryptWithPassword(key.getBytes(), newPassword,
keyStrengthener);
will(returnValue(newEncryptedKey)); will(returnValue(newEncryptedKey));
}}); }});

View File

@@ -96,7 +96,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
oneOf(db).transaction(with(false), withDbRunnable(txn)); oneOf(db).transaction(with(false), withDbRunnable(txn));
oneOf(metadataEncoder).encode(dictionary); oneOf(metadataEncoder).encode(dictionary);
will(returnValue(metadata)); will(returnValue(metadata));
oneOf(db).addLocalMessage(txn, message, metadata, shared, false); oneOf(db).addLocalMessage(txn, message, metadata, shared);
}}); }});
clientHelper.addLocalMessage(message, dictionary, shared); clientHelper.addLocalMessage(message, dictionary, shared);

View File

@@ -1,21 +1,16 @@
package org.briarproject.bramble.contact; package org.briarproject.bramble.contact;
import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
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.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestDatabaseConfigModule; import org.briarproject.bramble.test.TestDatabaseConfigModule;
import org.briarproject.bramble.test.TestDuplexTransportConnection; import org.briarproject.bramble.test.TestDuplexTransportConnection;
@@ -32,7 +27,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertNull; import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.fail; import static junit.framework.TestCase.fail;
import static org.briarproject.bramble.api.contact.PendingContactState.OFFLINE; import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.test.TestDuplexTransportConnection.createPair; import static org.briarproject.bramble.test.TestDuplexTransportConnection.createPair;
import static org.briarproject.bramble.test.TestPluginConfigModule.DUPLEX_TRANSPORT_ID; import static org.briarproject.bramble.test.TestPluginConfigModule.DUPLEX_TRANSPORT_ID;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
@@ -62,11 +57,11 @@ public class ContactExchangeIntegrationTest extends BrambleTestCase {
alice = DaggerContactExchangeIntegrationTestComponent.builder() alice = DaggerContactExchangeIntegrationTestComponent.builder()
.testDatabaseConfigModule( .testDatabaseConfigModule(
new TestDatabaseConfigModule(aliceDir)).build(); new TestDatabaseConfigModule(aliceDir)).build();
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(alice); alice.injectBrambleCoreEagerSingletons();
bob = DaggerContactExchangeIntegrationTestComponent.builder() bob = DaggerContactExchangeIntegrationTestComponent.builder()
.testDatabaseConfigModule(new TestDatabaseConfigModule(bobDir)) .testDatabaseConfigModule(new TestDatabaseConfigModule(bobDir))
.build(); .build();
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(bob); bob.injectBrambleCoreEagerSingletons();
// Set up the devices and get the identities // Set up the devices and get the identities
aliceIdentity = setUp(alice, "Alice"); aliceIdentity = setUp(alice, "Alice");
bobIdentity = setUp(bob, "Bob"); bobIdentity = setUp(bob, "Bob");
@@ -193,14 +188,9 @@ public class ContactExchangeIntegrationTest extends BrambleTestCase {
private PendingContact addPendingContact( private PendingContact addPendingContact(
ContactExchangeIntegrationTestComponent local, ContactExchangeIntegrationTestComponent local,
ContactExchangeIntegrationTestComponent remote) throws Exception { ContactExchangeIntegrationTestComponent remote) throws Exception {
EventWaiter waiter = new EventWaiter();
local.getEventBus().addListener(waiter);
String link = remote.getContactManager().getHandshakeLink(); String link = remote.getContactManager().getHandshakeLink();
String alias = remote.getIdentityManager().getLocalAuthor().getName(); String alias = remote.getIdentityManager().getLocalAuthor().getName();
PendingContact pendingContact = return local.getContactManager().addPendingContact(link, alias);
local.getContactManager().addPendingContact(link, alias);
waiter.latch.await(TIMEOUT, MILLISECONDS);
return pendingContact;
} }
private void assertContacts(boolean verified, private void assertContacts(boolean verified,
@@ -247,7 +237,7 @@ public class ContactExchangeIntegrationTest extends BrambleTestCase {
assertEquals(1, pairs.size()); assertEquals(1, pairs.size());
Pair<PendingContact, PendingContactState> pair = Pair<PendingContact, PendingContactState> pair =
pairs.iterator().next(); pairs.iterator().next();
assertEquals(OFFLINE, pair.getSecond()); assertEquals(WAITING_FOR_CONNECTION, pair.getSecond());
PendingContact pendingContact = pair.getFirst(); PendingContact pendingContact = pair.getFirst();
assertEquals(expectedIdentity.getLocalAuthor().getName(), assertEquals(expectedIdentity.getLocalAuthor().getName(),
pendingContact.getAlias()); pendingContact.getAlias());
@@ -271,19 +261,4 @@ public class ContactExchangeIntegrationTest extends BrambleTestCase {
tearDown(bob); tearDown(bob);
deleteTestDirectory(testDir); deleteTestDirectory(testDir);
} }
@NotNullByDefault
private static class EventWaiter implements EventListener {
private final CountDownLatch latch = new CountDownLatch(1);
@Override
public void eventOccurred(Event e) {
if (e instanceof PendingContactStateChangedEvent) {
PendingContactStateChangedEvent p =
(PendingContactStateChangedEvent) e;
if (p.getPendingContactState() == OFFLINE) latch.countDown();
}
}
}
} }

View File

@@ -21,8 +21,8 @@ public class PasswordBasedEncryptionTest extends BrambleTestCase {
public void testEncryptionAndDecryption() { public void testEncryptionAndDecryption() {
byte[] input = TestUtils.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);
byte[] output = crypto.decryptWithPassword(ciphertext, password, null); byte[] output = crypto.decryptWithPassword(ciphertext, password);
assertArrayEquals(input, output); assertArrayEquals(input, output);
} }
@@ -30,11 +30,11 @@ public class PasswordBasedEncryptionTest extends BrambleTestCase {
public void testInvalidCiphertextReturnsNull() { public void testInvalidCiphertextReturnsNull() {
byte[] input = TestUtils.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);
// Modify the ciphertext // Modify the ciphertext
int position = new Random().nextInt(ciphertext.length); int position = new Random().nextInt(ciphertext.length);
ciphertext[position] = (byte) (ciphertext[position] ^ 0xFF); ciphertext[position] = (byte) (ciphertext[position] ^ 0xFF);
byte[] output = crypto.decryptWithPassword(ciphertext, password, null); byte[] output = crypto.decryptWithPassword(ciphertext, password);
assertNull(output); assertNull(output);
} }
} }

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.system.SystemClock; import org.briarproject.bramble.system.SystemClock;
import org.briarproject.bramble.test.ArrayClock;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test; import org.junit.Test;
@@ -12,6 +11,8 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.assertTrue;
import org.briarproject.bramble.test.ArrayClock;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View File

@@ -61,12 +61,10 @@ import org.junit.Test;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Random;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap; import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
@@ -122,9 +120,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
private final Contact contact; private final Contact contact;
private final KeySetId keySetId; private final KeySetId keySetId;
private final PendingContactId pendingContactId; private final PendingContactId pendingContactId;
private final Random random = new Random();
private final boolean shared = random.nextBoolean();
private final boolean temporary = random.nextBoolean();
public DatabaseComponentImplTest() { public DatabaseComponentImplTest() {
clientId = getClientId(); clientId = getClientId();
@@ -258,8 +253,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
eventExecutor, shutdownManager); eventExecutor, shutdownManager);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addLocalMessage(transaction, message, metadata, shared, db.addLocalMessage(transaction, message, metadata, true));
temporary));
} }
@Test @Test
@@ -271,23 +265,20 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addMessage(txn, message, DELIVERED, shared, oneOf(database).addMessage(txn, message, DELIVERED, true, null);
temporary, null);
oneOf(database).mergeMessageMetadata(txn, messageId, metadata); oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
// The message was added, so the listeners should be called // The message was added, so the listeners should be called
oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class))); oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
oneOf(eventBus).broadcast(with(any( oneOf(eventBus)
MessageStateChangedEvent.class))); .broadcast(with(any(MessageStateChangedEvent.class)));
if (shared) oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class)));
oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); eventExecutor, shutdownManager);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addLocalMessage(transaction, message, metadata, shared, db.addLocalMessage(transaction, message, metadata, true));
temporary));
} }
@Test @Test
@@ -295,11 +286,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not) // Check whether the contact is in the DB (which it's not)
exactly(18).of(database).startTransaction(); exactly(16).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(18).of(database).containsContact(txn, contactId); exactly(16).of(database).containsContact(txn, contactId);
will(returnValue(false)); will(returnValue(false));
exactly(18).of(database).abortTransaction(txn); exactly(16).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); eventExecutor, shutdownManager);
@@ -377,14 +368,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// Expected // Expected
} }
try {
db.transaction(false, transaction ->
db.getSyncVersions(transaction, contactId));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try { try {
Ack a = new Ack(singletonList(messageId)); Ack a = new Ack(singletonList(messageId));
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -444,14 +427,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
} catch (NoSuchContactException expected) { } catch (NoSuchContactException expected) {
// Expected // Expected
} }
try {
db.transaction(false, transaction ->
db.setSyncVersions(transaction, contactId, emptyList()));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
} }
@Test @Test
@@ -594,11 +569,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the message is in the DB (which it's not) // Check whether the message is in the DB (which it's not)
exactly(12).of(database).startTransaction(); exactly(11).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(12).of(database).containsMessage(txn, messageId); exactly(11).of(database).containsMessage(txn, messageId);
will(returnValue(false)); will(returnValue(false));
exactly(12).of(database).abortTransaction(txn); exactly(11).of(database).abortTransaction(txn);
// Allow other checks to pass // Allow other checks to pass
allowing(database).containsContact(txn, contactId); allowing(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
@@ -662,14 +637,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// Expected // Expected
} }
try {
db.transaction(false, transaction ->
db.setMessagePermanent(transaction, message.getId()));
fail();
} catch (NoSuchMessageException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.setMessageShared(transaction, message.getId())); db.setMessageShared(transaction, message.getId()));
@@ -1005,8 +972,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(VISIBLE)); will(returnValue(VISIBLE));
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addMessage(txn, message, UNKNOWN, false, false, oneOf(database).addMessage(txn, message, UNKNOWN, false, contactId);
contactId);
// Second time // Second time
oneOf(database).containsContact(txn, contactId); oneOf(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
@@ -1541,7 +1507,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
public void testMessageDependencies() throws Exception { public void testMessageDependencies() throws Exception {
int shutdownHandle = 12345; int shutdownHandle = 12345;
MessageId messageId2 = new MessageId(getRandomId()); MessageId messageId2 = new MessageId(getRandomId());
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// open() // open()
oneOf(database).open(key, null); oneOf(database).open(key, null);
@@ -1556,8 +1521,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addMessage(txn, message, DELIVERED, shared, oneOf(database).addMessage(txn, message, DELIVERED, true, null);
temporary, null);
oneOf(database).mergeMessageMetadata(txn, messageId, metadata); oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
// addMessageDependencies() // addMessageDependencies()
oneOf(database).containsMessage(txn, messageId); oneOf(database).containsMessage(txn, messageId);
@@ -1580,8 +1544,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class))); oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
oneOf(eventBus).broadcast(with(any( oneOf(eventBus).broadcast(with(any(
MessageStateChangedEvent.class))); MessageStateChangedEvent.class)));
if (shared) oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class)));
oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class)));
// endTransaction() // endTransaction()
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
// close() // close()
@@ -1592,8 +1555,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
assertFalse(db.open(key, null)); assertFalse(db.open(key, null));
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
db.addLocalMessage(transaction, message, metadata, shared, db.addLocalMessage(transaction, message, metadata, true);
temporary);
Collection<MessageId> dependencies = new ArrayList<>(2); Collection<MessageId> dependencies = new ArrayList<>(2);
dependencies.add(messageId1); dependencies.add(messageId1);
dependencies.add(messageId2); dependencies.add(messageId2);

View File

@@ -567,9 +567,8 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
MessageState state = MessageState state =
MessageState.fromValue(random.nextInt(4)); MessageState.fromValue(random.nextInt(4));
boolean shared = random.nextBoolean(); boolean shared = random.nextBoolean();
boolean temporary = random.nextBoolean();
ContactId sender = random.nextBoolean() ? c : null; ContactId sender = random.nextBoolean() ? c : null;
db.addMessage(txn, m, state, shared, temporary, sender); db.addMessage(txn, m, state, shared, sender);
if (random.nextBoolean()) if (random.nextBoolean())
db.raiseRequestedFlag(txn, c, m.getId()); db.raiseRequestedFlag(txn, c, m.getId());
Metadata mm = getMetadata(METADATA_KEYS_PER_MESSAGE); Metadata mm = getMetadata(METADATA_KEYS_PER_MESSAGE);
@@ -598,8 +597,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
for (int j = 0; j < MESSAGES_PER_GROUP; j++) { for (int j = 0; j < MESSAGES_PER_GROUP; j++) {
Message m = getMessage(g.getId()); Message m = getMessage(g.getId());
messages.add(m); messages.add(m);
boolean temporary = random.nextBoolean(); db.addMessage(txn, m, DELIVERED, false, null);
db.addMessage(txn, m, DELIVERED, false, temporary, null);
Metadata mm = getMetadata(METADATA_KEYS_PER_MESSAGE); Metadata mm = getMetadata(METADATA_KEYS_PER_MESSAGE);
messageMeta.get(g.getId()).add(mm); messageMeta.get(g.getId()).add(mm);
db.mergeMessageMetadata(txn, m.getId(), mm); db.mergeMessageMetadata(txn, m.getId(), mm);

View File

@@ -34,7 +34,6 @@ import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.SettableClock; import org.briarproject.bramble.test.SettableClock;
import org.briarproject.bramble.test.TestDatabaseConfig; import org.briarproject.bramble.test.TestDatabaseConfig;
import org.briarproject.bramble.test.TestMessageFactory; import org.briarproject.bramble.test.TestMessageFactory;
import org.briarproject.bramble.test.TestUtils;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -42,6 +41,7 @@ import org.junit.Test;
import java.io.File; import java.io.File;
import java.sql.Connection; import java.sql.Connection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -51,7 +51,6 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap; import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
@@ -154,7 +153,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addGroup(txn, group); db.addGroup(txn, group);
assertTrue(db.containsGroup(txn, groupId)); assertTrue(db.containsGroup(txn, groupId));
assertFalse(db.containsMessage(txn, messageId)); assertFalse(db.containsMessage(txn, messageId));
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
assertTrue(db.containsMessage(txn, messageId)); assertTrue(db.containsMessage(txn, messageId));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -192,7 +191,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and a message // Add a group and a message
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// Removing the group should remove the message // Removing the group should remove the message
assertTrue(db.containsMessage(txn, messageId)); assertTrue(db.containsMessage(txn, messageId));
@@ -214,7 +213,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// The contact has not seen the message, so it should be sendable // The contact has not seen the message, so it should be sendable
Collection<MessageId> ids = Collection<MessageId> ids =
@@ -245,7 +244,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, UNKNOWN, true, false, null); db.addMessage(txn, message, UNKNOWN, true, null);
// The message has not been validated, so it should not be sendable // The message has not been validated, so it should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -289,7 +288,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// The group is invisible, so the message should not be sendable // The group is invisible, so the message should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -341,7 +340,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, false, false, null); db.addMessage(txn, message, DELIVERED, false, null);
// The message is not shared, so it should not be sendable // The message is not shared, so it should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -372,7 +371,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// The message is sendable, but too large to send // The message is sendable, but too large to send
Collection<MessageId> ids = Collection<MessageId> ids =
@@ -403,15 +402,15 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add some messages to ack // Add some messages to ack
Message message1 = getMessage(groupId); Message message1 = getMessage(groupId);
MessageId messageId1 = message1.getId(); MessageId messageId1 = message1.getId();
db.addMessage(txn, message, DELIVERED, true, false, contactId); db.addMessage(txn, message, DELIVERED, true, contactId);
db.addMessage(txn, message1, DELIVERED, true, false, contactId); db.addMessage(txn, message1, DELIVERED, true, contactId);
// Both message IDs should be returned // Both message IDs should be returned
Collection<MessageId> ids = db.getMessagesToAck(txn, contactId, 1234); Collection<MessageId> ids = db.getMessagesToAck(txn, contactId, 1234);
assertEquals(asList(messageId, messageId1), ids); assertEquals(Arrays.asList(messageId, messageId1), ids);
// Remove both message IDs // Remove both message IDs
db.lowerAckFlag(txn, contactId, asList(messageId, messageId1)); db.lowerAckFlag(txn, contactId, Arrays.asList(messageId, messageId1));
// Both message IDs should have been removed // Both message IDs should have been removed
assertEquals(emptyList(), db.getMessagesToAck(txn, assertEquals(emptyList(), db.getMessagesToAck(txn,
@@ -423,7 +422,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Both message IDs should be returned // Both message IDs should be returned
ids = db.getMessagesToAck(txn, contactId, 1234); ids = db.getMessagesToAck(txn, contactId, 1234);
assertEquals(asList(messageId, messageId1), ids); assertEquals(Arrays.asList(messageId, messageId1), ids);
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -440,7 +439,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// Retrieve the message from the database and mark it as sent // Retrieve the message from the database and mark it as sent
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
@@ -609,7 +608,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// The group is not visible so the message should not be visible // The group is not visible so the message should not be visible
assertFalse(db.containsVisibleMessage(txn, contactId, messageId)); assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
@@ -1150,43 +1149,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testGetContactsByHandshakePublicKey() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add an identity for a local author - no contacts should be
// associated
db.addIdentity(txn, identity);
PublicKey handshakePublicKey = TestUtils.getSignaturePublicKey();
Contact contact =
db.getContact(txn, handshakePublicKey, localAuthor.getId());
assertNull(contact);
// Add a contact associated with the local author
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
handshakePublicKey, true));
contact = db.getContact(txn, handshakePublicKey, localAuthor.getId());
assertNotNull(contact);
assertEquals(contactId, contact.getId());
assertEquals(author, contact.getAuthor());
assertNull(contact.getAlias());
assertEquals(handshakePublicKey, contact.getHandshakePublicKey());
assertTrue(contact.isVerified());
assertEquals(author.getName(), contact.getAuthor().getName());
assertEquals(author.getPublicKey(), contact.getAuthor().getPublicKey());
assertEquals(author.getFormatVersion(),
contact.getAuthor().getFormatVersion());
// Ensure no contacts are returned after contact was deleted
db.removeContact(txn, contactId);
contact = db.getContact(txn, handshakePublicKey, localAuthor.getId());
assertNull(contact);
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testOfferedMessages() throws Exception { public void testOfferedMessages() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
@@ -1261,7 +1223,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and a message // Add a group and a message
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// Attach some metadata to the message // Attach some metadata to the message
Metadata metadata = new Metadata(); Metadata metadata = new Metadata();
@@ -1332,7 +1294,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and a message // Add a group and a message
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// Attach some metadata to the message // Attach some metadata to the message
Metadata metadata = new Metadata(); Metadata metadata = new Metadata();
@@ -1393,8 +1355,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and two messages // Add a group and two messages
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
db.addMessage(txn, message1, DELIVERED, true, false, null); db.addMessage(txn, message1, DELIVERED, true, null);
// Attach some metadata to the messages // Attach some metadata to the messages
Metadata metadata = new Metadata(); Metadata metadata = new Metadata();
@@ -1497,8 +1459,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and two messages // Add a group and two messages
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
db.addMessage(txn, message1, DELIVERED, true, false, null); db.addMessage(txn, message1, DELIVERED, true, null);
// Attach some metadata to the messages // Attach some metadata to the messages
Metadata metadata = new Metadata(); Metadata metadata = new Metadata();
@@ -1574,9 +1536,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and some messages // Add a group and some messages
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, PENDING, true, false, contactId); db.addMessage(txn, message, PENDING, true, contactId);
db.addMessage(txn, message1, PENDING, true, false, contactId); db.addMessage(txn, message1, PENDING, true, contactId);
db.addMessage(txn, message2, INVALID, true, false, contactId); db.addMessage(txn, message2, INVALID, true, contactId);
// Add dependencies // Add dependencies
db.addMessageDependency(txn, message, messageId1, PENDING); db.addMessageDependency(txn, message, messageId1, PENDING);
@@ -1627,7 +1589,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(0, dependents.size()); assertEquals(0, dependents.size());
// Add message 3 // Add message 3
db.addMessage(txn, message3, UNKNOWN, false, false, contactId); db.addMessage(txn, message3, UNKNOWN, false, contactId);
// Message 3 has message 1 as a dependent // Message 3 has message 1 as a dependent
dependents = db.getMessageDependents(txn, messageId3); dependents = db.getMessageDependents(txn, messageId3);
@@ -1639,7 +1601,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(0, dependents.size()); assertEquals(0, dependents.size());
// Add message 4 // Add message 4
db.addMessage(txn, message4, UNKNOWN, false, false, contactId); db.addMessage(txn, message4, UNKNOWN, false, contactId);
// Message 4 has message 2 as a dependent // Message 4 has message 2 as a dependent
dependents = db.getMessageDependents(txn, messageId4); dependents = db.getMessageDependents(txn, messageId4);
@@ -1657,7 +1619,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and a message // Add a group and a message
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, PENDING, true, false, contactId); db.addMessage(txn, message, PENDING, true, contactId);
// Add a second group // Add a second group
Group group1 = getGroup(clientId, 123); Group group1 = getGroup(clientId, 123);
@@ -1667,7 +1629,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a message to the second group // Add a message to the second group
Message message1 = getMessage(groupId1); Message message1 = getMessage(groupId1);
MessageId messageId1 = message1.getId(); MessageId messageId1 = message1.getId();
db.addMessage(txn, message1, DELIVERED, true, false, contactId); db.addMessage(txn, message1, DELIVERED, true, contactId);
// Create an ID for a missing message // Create an ID for a missing message
MessageId messageId2 = new MessageId(getRandomId()); MessageId messageId2 = new MessageId(getRandomId());
@@ -1675,7 +1637,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add another message to the first group // Add another message to the first group
Message message3 = getMessage(groupId); Message message3 = getMessage(groupId);
MessageId messageId3 = message3.getId(); MessageId messageId3 = message3.getId();
db.addMessage(txn, message3, DELIVERED, true, false, contactId); db.addMessage(txn, message3, DELIVERED, true, contactId);
// Add dependencies between the messages // Add dependencies between the messages
db.addMessageDependency(txn, message, messageId1, PENDING); db.addMessageDependency(txn, message, messageId1, PENDING);
@@ -1718,10 +1680,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and some messages with different states // Add a group and some messages with different states
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message1, UNKNOWN, true, false, contactId); db.addMessage(txn, message1, UNKNOWN, true, contactId);
db.addMessage(txn, message2, INVALID, true, false, contactId); db.addMessage(txn, message2, INVALID, true, contactId);
db.addMessage(txn, message3, PENDING, true, false, contactId); db.addMessage(txn, message3, PENDING, true, contactId);
db.addMessage(txn, message4, DELIVERED, true, false, contactId); db.addMessage(txn, message4, DELIVERED, true, contactId);
Collection<MessageId> result; Collection<MessageId> result;
@@ -1751,10 +1713,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and some messages // Add a group and some messages
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message1, DELIVERED, true, false, contactId); db.addMessage(txn, message1, DELIVERED, true, contactId);
db.addMessage(txn, message2, DELIVERED, false, false, contactId); db.addMessage(txn, message2, DELIVERED, false, contactId);
db.addMessage(txn, message3, DELIVERED, false, false, contactId); db.addMessage(txn, message3, DELIVERED, false, contactId);
db.addMessage(txn, message4, DELIVERED, true, false, contactId); db.addMessage(txn, message4, DELIVERED, true, contactId);
// Introduce dependencies between the messages // Introduce dependencies between the messages
db.addMessageDependency(txn, message1, message2.getId(), DELIVERED); db.addMessageDependency(txn, message1, message2.getId(), DELIVERED);
@@ -1782,7 +1744,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// The message should not be sent or seen // The message should not be sent or seen
MessageStatus status = db.getMessageStatus(txn, contactId, messageId); MessageStatus status = db.getMessageStatus(txn, contactId, messageId);
@@ -1916,7 +1878,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// The message should be visible to the contact // The message should be visible to the contact
assertTrue(db.containsVisibleMessage(txn, contactId, messageId)); assertTrue(db.containsVisibleMessage(txn, contactId, messageId));
@@ -1999,7 +1961,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add a group and a message // Add a group and a message
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, UNKNOWN, false, false, contactId); db.addMessage(txn, message, UNKNOWN, false, contactId);
// Walk the message through the validation and delivery states // Walk the message through the validation and delivery states
assertEquals(UNKNOWN, db.getMessageState(txn, messageId)); assertEquals(UNKNOWN, db.getMessageState(txn, messageId));
@@ -2026,7 +1988,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(contactId, assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, UNKNOWN, false, false, null); db.addMessage(txn, message, UNKNOWN, false, null);
// There should be no messages to send // There should be no messages to send
assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId)); assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId));
@@ -2111,7 +2073,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// Time: now // Time: now
// Retrieve the message from the database // Retrieve the message from the database
@@ -2156,7 +2118,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addContact(txn, author, localAuthor.getId(), null, true)); db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, false, null); db.addMessage(txn, message, DELIVERED, true, null);
// Time: now // Time: now
// Retrieve the message from the database // Retrieve the message from the database
@@ -2295,58 +2257,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testTemporaryMessages() throws Exception {
Message message1 = getMessage(groupId);
MessageId messageId1 = message1.getId();
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a group and two temporary messages
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, false, true, null);
db.addMessage(txn, message1, DELIVERED, false, true, null);
// Mark one of the messages as permanent
db.setMessagePermanent(txn, messageId);
// Remove all temporary messages
db.removeTemporaryMessages(txn);
// The permanent message should not have been removed
assertTrue(db.containsMessage(txn, messageId));
// The temporary message should have been removed
assertFalse(db.containsMessage(txn, messageId1));
db.commitTransaction(txn);
db.close();
}
@Test
public void testSyncVersions() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact
db.addIdentity(txn, identity);
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), null, true));
// Only sync version 0 should be supported by default
List<Byte> defaultSupported = singletonList((byte) 0);
assertEquals(defaultSupported, db.getSyncVersions(txn, contactId));
// Set the supported versions and check that they're returned
List<Byte> supported = asList((byte) 0, (byte) 1);
db.setSyncVersions(txn, contactId, supported);
assertEquals(supported, db.getSyncVersions(txn, contactId));
db.commitTransaction(txn);
db.close();
}
private Database<Connection> open(boolean resume) throws Exception { private Database<Connection> open(boolean resume) throws Exception {
return open(resume, new TestMessageFactory(), new SystemClock()); return open(resume, new TestMessageFactory(), new SystemClock());
} }

View File

@@ -42,7 +42,6 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
oneOf(db).open(dbKey, lifecycleManager); oneOf(db).open(dbKey, lifecycleManager);
will(returnValue(false)); will(returnValue(false));
oneOf(db).transaction(with(false), withDbRunnable(txn)); oneOf(db).transaction(with(false), withDbRunnable(txn));
oneOf(db).removeTemporaryMessages(txn);
allowing(eventBus).broadcast(with(any(LifecycleEvent.class))); allowing(eventBus).broadcast(with(any(LifecycleEvent.class)));
}}); }});

View File

@@ -13,8 +13,8 @@ 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.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.TransportActiveEvent; import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
@@ -38,7 +38,7 @@ import static java.util.Collections.emptyList;
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 java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.test.ListMatcher.listOf; import static org.briarproject.bramble.test.CollectionMatcher.collectionOf;
import static org.briarproject.bramble.test.PairMatcher.pairOf; import static org.briarproject.bramble.test.PairMatcher.pairOf;
import static org.briarproject.bramble.test.TestUtils.getContactId; import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -322,7 +322,7 @@ public class PollerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testPollsOnTransportActivated() throws Exception { public void testPollsOnTransportEnabled() throws Exception {
DuplexPlugin plugin = context.mock(DuplexPlugin.class); DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -351,20 +351,17 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) (pollingInterval * 0.5)), with(MILLISECONDS)); with((long) (pollingInterval * 0.5)), with(MILLISECONDS));
will(returnValue(future)); will(returnValue(future));
// FIXME: Revert
oneOf(plugin).supportsDiscovery();
will(returnValue(false));
// 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).getConnectedContacts(transportId); oneOf(connectionRegistry).getConnectedContacts(transportId);
will(returnValue(emptyList())); will(returnValue(emptyList()));
// Poll the plugin // Poll the plugin
oneOf(plugin).poll(with(listOf(pairOf( oneOf(plugin).poll(with(collectionOf(
equal(properties), any(ConnectionHandler.class))))); pairOf(equal(properties), any(ConnectionHandler.class)))));
}}); }});
poller.eventOccurred(new TransportActiveEvent(transportId)); poller.eventOccurred(new TransportEnabledEvent(transportId));
} }
@Test @Test
@@ -397,9 +394,6 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) (pollingInterval * 0.5)), with(MILLISECONDS)); with((long) (pollingInterval * 0.5)), with(MILLISECONDS));
will(returnValue(future)); will(returnValue(future));
// FIXME: Revert
oneOf(plugin).supportsDiscovery();
will(returnValue(false));
// 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)));
@@ -408,11 +402,11 @@ public class PollerImplTest extends BrambleMockTestCase {
// All contacts are connected, so don't poll the plugin // All contacts are connected, so don't poll the plugin
}}); }});
poller.eventOccurred(new TransportActiveEvent(transportId)); poller.eventOccurred(new TransportEnabledEvent(transportId));
} }
@Test @Test
public void testCancelsPollingOnTransportDeactivated() { public void testCancelsPollingOnTransportDisabled() {
Plugin plugin = context.mock(Plugin.class); Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -430,11 +424,11 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L), oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L),
with(MILLISECONDS)); with(MILLISECONDS));
will(returnValue(future)); will(returnValue(future));
// The plugin is deactivated before the task runs - cancel the task // The plugin is disabled before the task runs - cancel the task
oneOf(future).cancel(false); oneOf(future).cancel(false);
}}); }});
poller.eventOccurred(new TransportActiveEvent(transportId)); poller.eventOccurred(new TransportEnabledEvent(transportId));
poller.eventOccurred(new TransportInactiveEvent(transportId)); poller.eventOccurred(new TransportDisabledEvent(transportId));
} }
} }

View File

@@ -4,15 +4,15 @@ import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
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.Plugin.State;
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;
@@ -22,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;
@@ -32,90 +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.api.plugin.Plugin.PREF_PLUGIN_ENABLE;
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) {
@@ -126,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));
@@ -155,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));
@@ -198,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 =
@@ -240,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));
@@ -285,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;
@@ -299,20 +340,13 @@ 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();
private final Settings settings = new Settings();
private Callback() {
settings.putBoolean(PREF_PLUGIN_ENABLE, true);
}
@Override @Override
public Settings getSettings() { public Settings getSettings() {
return settings; return new Settings();
} }
@Override @Override
@@ -331,7 +365,11 @@ public class LanTcpPluginTest extends BrambleTestCase {
} }
@Override @Override
public void pluginStateChanged(State newState) { public void transportEnabled() {
}
@Override
public void transportDisabled() {
} }
@Override @Override

View File

@@ -637,8 +637,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
will(returnValue(timestamp)); will(returnValue(timestamp));
oneOf(clientHelper).createMessage(g, timestamp, body); oneOf(clientHelper).createMessage(g, timestamp, body);
will(returnValue(message)); will(returnValue(message));
oneOf(clientHelper).addLocalMessage(txn, message, meta, shared, oneOf(clientHelper).addLocalMessage(txn, message, meta, shared);
false);
}}); }});
} }
} }

View File

@@ -17,8 +17,8 @@ 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;
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint; import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
@@ -45,11 +45,10 @@ import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.api.contact.PendingContactState.ADDING_CONTACT; import static org.briarproject.bramble.api.contact.PendingContactState.ADDING_CONTACT;
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED; import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
import static org.briarproject.bramble.api.contact.PendingContactState.OFFLINE;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION; import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.rendezvous.RendezvousConstants.POLLING_INTERVAL_MS; import static org.briarproject.bramble.rendezvous.RendezvousConstants.POLLING_INTERVAL_MS;
import static org.briarproject.bramble.rendezvous.RendezvousConstants.RENDEZVOUS_TIMEOUT_MS; import static org.briarproject.bramble.rendezvous.RendezvousConstants.RENDEZVOUS_TIMEOUT_MS;
import static org.briarproject.bramble.test.ListMatcher.listOf; import static org.briarproject.bramble.test.CollectionMatcher.collectionOf;
import static org.briarproject.bramble.test.PairMatcher.pairOf; import static org.briarproject.bramble.test.PairMatcher.pairOf;
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey; import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey; import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
@@ -121,7 +120,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
will(returnValue(beforeExpiry)); will(returnValue(beforeExpiry));
oneOf(eventBus).broadcast(with(new PredicateMatcher<>( oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
PendingContactStateChangedEvent.class, e -> PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == OFFLINE))); e.getPendingContactState() == WAITING_FOR_CONNECTION)));
// Capture the poll task // Capture the poll task
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)), oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
@@ -178,14 +177,14 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
rendezvousPoller.startService(); rendezvousPoller.startService();
context.assertIsSatisfied(); context.assertIsSatisfied();
// Activate the transport - no endpoints should be created yet // Enable the transport - no endpoints should be created yet
expectGetPlugin(); expectGetPlugin();
rendezvousPoller.eventOccurred(new TransportActiveEvent(transportId)); rendezvousPoller.eventOccurred(new TransportEnabledEvent(transportId));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Add the pending contact - endpoint should be created and polled // Add the pending contact - endpoint should be created and polled
expectAddPendingContact(beforeExpiry, WAITING_FOR_CONNECTION); expectAddUnexpiredPendingContact(beforeExpiry);
expectDeriveRendezvousKey(); expectDeriveRendezvousKey();
expectCreateEndpoint(); expectCreateEndpoint();
@@ -196,7 +195,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(beforeExpiry)); will(returnValue(beforeExpiry));
oneOf(eventBus).broadcast(with(any(RendezvousPollEvent.class))); oneOf(eventBus).broadcast(with(any(RendezvousPollEvent.class)));
oneOf(plugin).poll(with(listOf(pairOf( oneOf(plugin).poll(with(collectionOf(pairOf(
equal(transportProperties), equal(transportProperties),
any(ConnectionHandler.class))))); any(ConnectionHandler.class)))));
}}); }});
@@ -206,14 +205,16 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
context.assertIsSatisfied(); context.assertIsSatisfied();
// Remove the pending contact - endpoint should be closed // Remove the pending contact - endpoint should be closed
expectCloseEndpoint(); context.checking(new Expectations() {{
oneOf(rendezvousEndpoint).close();
}});
rendezvousPoller.eventOccurred( rendezvousPoller.eventOccurred(
new PendingContactRemovedEvent(pendingContact.getId())); new PendingContactRemovedEvent(pendingContact.getId()));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Deactivate the transport - endpoint is already closed // Disable the transport - endpoint is already closed
rendezvousPoller.eventOccurred(new TransportInactiveEvent(transportId)); rendezvousPoller.eventOccurred(new TransportDisabledEvent(transportId));
} }
@Test @Test
@@ -230,14 +231,14 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
rendezvousPoller.startService(); rendezvousPoller.startService();
context.assertIsSatisfied(); context.assertIsSatisfied();
// Activate the transport - no endpoints should be created yet // Enable the transport - no endpoints should be created yet
expectGetPlugin(); expectGetPlugin();
rendezvousPoller.eventOccurred(new TransportActiveEvent(transportId)); rendezvousPoller.eventOccurred(new TransportEnabledEvent(transportId));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Add the pending contact - endpoint should be created and polled // Add the pending contact - endpoint should be created and polled
expectAddPendingContact(beforeExpiry, WAITING_FOR_CONNECTION); expectAddUnexpiredPendingContact(beforeExpiry);
expectDeriveRendezvousKey(); expectDeriveRendezvousKey();
expectCreateEndpoint(); expectCreateEndpoint();
@@ -248,7 +249,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(beforeExpiry)); will(returnValue(beforeExpiry));
oneOf(eventBus).broadcast(with(any(RendezvousPollEvent.class))); oneOf(eventBus).broadcast(with(any(RendezvousPollEvent.class)));
oneOf(plugin).poll(with(listOf(pairOf( oneOf(plugin).poll(with(collectionOf(pairOf(
equal(transportProperties), equal(transportProperties),
any(ConnectionHandler.class))))); any(ConnectionHandler.class)))));
}}); }});
@@ -259,7 +260,10 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
// Run the poll task - pending contact expires, endpoint is closed // Run the poll task - pending contact expires, endpoint is closed
expectPendingContactExpires(afterExpiry); expectPendingContactExpires(afterExpiry);
expectCloseEndpoint();
context.checking(new Expectations() {{
oneOf(rendezvousEndpoint).close();
}});
capturePollTask.get().run(); capturePollTask.get().run();
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -269,12 +273,12 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
new PendingContactRemovedEvent(pendingContact.getId())); new PendingContactRemovedEvent(pendingContact.getId()));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Deactivate the transport - endpoint is already closed // Disable the transport - endpoint is already closed
rendezvousPoller.eventOccurred(new TransportInactiveEvent(transportId)); rendezvousPoller.eventOccurred(new TransportDisabledEvent(transportId));
} }
@Test @Test
public void testCreatesAndClosesEndpointsWhenTransportIsActivatedAndDeactivated() public void testCreatesAndClosesEndpointsWhenTransportIsEnabledAndDisabled()
throws Exception { throws Exception {
long beforeExpiry = pendingContact.getTimestamp(); long beforeExpiry = pendingContact.getTimestamp();
@@ -285,26 +289,26 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
context.assertIsSatisfied(); context.assertIsSatisfied();
// Add the pending contact - no endpoints should be created yet // Add the pending contact - no endpoints should be created yet
expectAddPendingContact(beforeExpiry, OFFLINE); expectAddUnexpiredPendingContact(beforeExpiry);
expectDeriveRendezvousKey(); expectDeriveRendezvousKey();
rendezvousPoller.eventOccurred( rendezvousPoller.eventOccurred(
new PendingContactAddedEvent(pendingContact)); new PendingContactAddedEvent(pendingContact));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Activate the transport - endpoint should be created // Enable the transport - endpoint should be created
expectGetPlugin(); expectGetPlugin();
expectCreateEndpoint(); expectCreateEndpoint();
expectStateChangedEvent(WAITING_FOR_CONNECTION);
rendezvousPoller.eventOccurred(new TransportActiveEvent(transportId)); rendezvousPoller.eventOccurred(new TransportEnabledEvent(transportId));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Deactivate the transport - endpoint should be closed // Disable the transport - endpoint should be closed
expectCloseEndpoint(); context.checking(new Expectations() {{
expectStateChangedEvent(OFFLINE); oneOf(rendezvousEndpoint).close();
}});
rendezvousPoller.eventOccurred(new TransportInactiveEvent(transportId)); rendezvousPoller.eventOccurred(new TransportDisabledEvent(transportId));
context.assertIsSatisfied(); context.assertIsSatisfied();
// Remove the pending contact - endpoint is already closed // Remove the pending contact - endpoint is already closed
@@ -478,14 +482,13 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
return capturePollTask; return capturePollTask;
} }
private void expectAddPendingContact(long now, private void expectAddUnexpiredPendingContact(long now) {
PendingContactState initialState) {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
will(returnValue(now)); will(returnValue(now));
oneOf(eventBus).broadcast(with(new PredicateMatcher<>( oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
PendingContactStateChangedEvent.class, e -> PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == initialState))); e.getPendingContactState() == WAITING_FOR_CONNECTION)));
}}); }});
} }
@@ -543,7 +546,7 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
will(returnValue(now)); will(returnValue(now));
oneOf(eventBus).broadcast(with(new PredicateMatcher<>( oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
PendingContactStateChangedEvent.class, e -> PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == OFFLINE))); e.getPendingContactState() == WAITING_FOR_CONNECTION)));
// Capture the poll task // Capture the poll task
oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)), oneOf(scheduler).scheduleAtFixedRate(with(any(Runnable.class)),
with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS), with(POLLING_INTERVAL_MS),
@@ -573,10 +576,4 @@ public class RendezvousPollerImplTest extends BrambleMockTestCase {
e.getPendingContactState() == state))); e.getPendingContactState() == state)));
}}); }});
} }
private void expectCloseEndpoint() throws Exception {
context.checking(new Expectations() {{
oneOf(rendezvousEndpoint).close();
}});
}
} }

View File

@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
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.transport.StreamWriter; import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
@@ -50,8 +49,6 @@ public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
// Add listener // Add listener
oneOf(eventBus).addListener(session); oneOf(eventBus).addListener(session);
// Send the protocol versions
oneOf(recordWriter).writeVersions(with(any(Versions.class)));
// No acks to send // No acks to send
oneOf(db).transactionWithNullableResult(with(false), oneOf(db).transactionWithNullableResult(with(false),
withNullableDbCallable(noAckTxn)); withNullableDbCallable(noAckTxn));
@@ -86,8 +83,6 @@ public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
// Add listener // Add listener
oneOf(eventBus).addListener(session); oneOf(eventBus).addListener(session);
// Send the protocol versions
oneOf(recordWriter).writeVersions(with(any(Versions.class)));
// One ack to send // One ack to send
oneOf(db).transactionWithNullableResult(with(false), oneOf(db).transactionWithNullableResult(with(false),
withNullableDbCallable(ackTxn)); withNullableDbCallable(ackTxn));

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