mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
1 Commits
alpha-1.5.
...
snowflake-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50538b6908 |
@@ -83,19 +83,30 @@ android test:
|
||||
test_reproducible:
|
||||
stage: check_reproducibility
|
||||
script:
|
||||
- "curl -X POST -F token=${RELEASE_CHECK_TOKEN} -F ref=master -F variables[APP]='briar' -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/61/trigger/pipeline"
|
||||
- "curl -X POST -F token=${RELEASE_JAR_CHECK_TOKEN} -F ref=main -F variables[APP]='briar-headless' -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/307/trigger/pipeline"
|
||||
- "curl -X POST -F token=${RELEASE_CHECK_TOKEN} -F ref=master -F variables[RELEASE_TAG]=${CI_COMMIT_REF_NAME} https://code.briarproject.org/api/v4/projects/61/trigger/pipeline"
|
||||
only:
|
||||
- tags
|
||||
|
||||
mailbox integration test:
|
||||
.optional_tests:
|
||||
stage: optional_tests
|
||||
extends: .base-test
|
||||
|
||||
bridge test:
|
||||
extends: .optional_tests
|
||||
rules:
|
||||
- changes:
|
||||
- mailbox-integration-tests/**/*
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
when: on_success
|
||||
allow_failure: false
|
||||
- if: '$CI_COMMIT_TAG == null'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
script:
|
||||
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
|
||||
timeout: 4h
|
||||
|
||||
mailbox integration test:
|
||||
extends: .optional_tests
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
when: on_success
|
||||
- if: '$CI_COMMIT_TAG == null'
|
||||
|
||||
3
.idea/codeStyles/Project.xml
generated
3
.idea/codeStyles/Project.xml
generated
@@ -35,6 +35,9 @@
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="Groovy">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
|
||||
28
.idea/runConfigurations/BridgeTest.xml
generated
Normal file
28
.idea/runConfigurations/BridgeTest.xml
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="BridgeTest" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="env">
|
||||
<map>
|
||||
<entry key="OPTIONAL_TESTS" value="org.briarproject.bramble.plugin.tor.BridgeTest" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="--tests "org.briarproject.bramble.plugin.tor.BridgeTest"" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":bramble-java:test" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,10 +1,12 @@
|
||||
import com.android.build.gradle.tasks.MergeResources
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'witness'
|
||||
apply from: 'witness.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
buildToolsVersion '33.0.0'
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.3'
|
||||
|
||||
packagingOptions {
|
||||
doNotStrip '**/*.so'
|
||||
@@ -12,13 +14,12 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 31
|
||||
versionCode 10500
|
||||
versionName "1.5.0"
|
||||
targetSdkVersion 30
|
||||
versionCode 10412
|
||||
versionName "1.4.12"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunnerArguments disableAnalytics: 'true'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@@ -40,19 +41,8 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api 'org.briarproject:dont-kill-me-lib:0.2.6'
|
||||
|
||||
// In theory this dependency shouldn't be needed, but without it Android Studio's linter will
|
||||
// complain about unresolved symbols for bramble-api test classes in bramble-android tests,
|
||||
// even though the bramble-api test classes are provided by the testImplementation dependency
|
||||
// below and the compiler can find them
|
||||
implementation project(':bramble-api')
|
||||
|
||||
implementation project(':bramble-core')
|
||||
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
implementation 'androidx.annotation:annotation:1.5.0'
|
||||
implementation "org.briarproject:onionwrapper-android:$onionwrapper_version"
|
||||
|
||||
tor "org.briarproject:tor-android:$tor_version"
|
||||
tor "org.briarproject:obfs4proxy-android:$obfs4proxy_version"
|
||||
tor "org.briarproject:snowflake-android:$snowflake_version"
|
||||
@@ -62,33 +52,61 @@ dependencies {
|
||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testImplementation "org.jmock:jmock:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
||||
}
|
||||
|
||||
def torBinariesDir = 'src/main/res/raw'
|
||||
def torLibsDir = 'src/main/jniLibs'
|
||||
|
||||
task cleanTorBinaries {
|
||||
outputs.dir torLibsDir
|
||||
doLast {
|
||||
delete fileTree(torLibsDir)
|
||||
delete fileTree(torBinariesDir) { include '*.zip' }
|
||||
delete fileTree(torLibsDir) { include '**/*.so' }
|
||||
}
|
||||
}
|
||||
|
||||
clean.dependsOn cleanTorBinaries
|
||||
|
||||
task unpackTorBinaries {
|
||||
outputs.dir torLibsDir
|
||||
doLast {
|
||||
copy {
|
||||
from configurations.tor.collect { zipTree(it) }
|
||||
into torLibsDir
|
||||
configurations.tor.each { outer ->
|
||||
zipTree(outer).each { inner ->
|
||||
if (inner.name.endsWith('_arm_pie.zip')) {
|
||||
copy {
|
||||
from zipTree(inner)
|
||||
into torLibsDir
|
||||
rename '(.*)', 'armeabi-v7a/lib$1.so'
|
||||
}
|
||||
} else if (inner.name.endsWith('_arm64_pie.zip')) {
|
||||
copy {
|
||||
from zipTree(inner)
|
||||
into torLibsDir
|
||||
rename '(.*)', 'arm64-v8a/lib$1.so'
|
||||
}
|
||||
} else if (inner.name.endsWith('_x86_pie.zip')) {
|
||||
copy {
|
||||
from zipTree(inner)
|
||||
into torLibsDir
|
||||
rename '(.*)', 'x86/lib$1.so'
|
||||
}
|
||||
} else if (inner.name.endsWith('_x86_64_pie.zip')) {
|
||||
copy {
|
||||
from zipTree(inner)
|
||||
into torLibsDir
|
||||
rename '(.*)', 'x86_64/lib$1.so'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dependsOn cleanTorBinaries
|
||||
}
|
||||
|
||||
preBuild.dependsOn unpackTorBinaries
|
||||
tasks.withType(MergeResources) {
|
||||
inputs.dir torBinariesDir
|
||||
inputs.dir torLibsDir
|
||||
dependsOn unpackTorBinaries
|
||||
}
|
||||
|
||||
@@ -1,28 +1,15 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.briarproject.bramble">
|
||||
<manifest
|
||||
package="org.briarproject.bramble"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth"
|
||||
android:required="false" />
|
||||
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<!-- The BLUETOOTH permission was supposed to be removed in API 31 but is still needed on some Xiaomi/Redmi/POCO devices running API 31 and Nubia devices running API 32 -->
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_ADMIN"
|
||||
android:maxSdkVersion="30" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_SCAN"
|
||||
android:usesPermissionFlags="neverForLocation"
|
||||
tools:targetApi="31" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.briarproject.android.dontkillmelib.wakelock;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class AndroidWakeLockModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
AndroidWakeLockManager provideWakeLockManager(
|
||||
AndroidWakeLockManagerImpl wakeLockManager) {
|
||||
return wakeLockManager;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
package org.briarproject.bramble;
|
||||
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockModule;
|
||||
import org.briarproject.bramble.battery.AndroidBatteryModule;
|
||||
import org.briarproject.bramble.io.DnsModule;
|
||||
import org.briarproject.bramble.network.AndroidNetworkModule;
|
||||
import org.briarproject.bramble.plugin.tor.CircumventionModule;
|
||||
import org.briarproject.bramble.reporting.ReportingModule;
|
||||
import org.briarproject.bramble.socks.SocksModule;
|
||||
import org.briarproject.bramble.system.AndroidSystemModule;
|
||||
import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
|
||||
import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
|
||||
import org.briarproject.bramble.system.DefaultThreadFactoryModule;
|
||||
import org.briarproject.onionwrapper.CircumventionModule;
|
||||
|
||||
import dagger.Module;
|
||||
|
||||
@@ -20,8 +18,6 @@ import dagger.Module;
|
||||
AndroidSystemModule.class,
|
||||
AndroidTaskSchedulerModule.class,
|
||||
AndroidWakefulIoExecutorModule.class,
|
||||
AndroidWakeLockModule.class,
|
||||
DefaultThreadFactoryModule.class,
|
||||
CircumventionModule.class,
|
||||
DnsModule.class,
|
||||
ReportingModule.class,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.briarproject.bramble.api.system;
|
||||
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface AndroidWakeLock {
|
||||
|
||||
/**
|
||||
* Acquires the wake lock. This has no effect if the wake lock has already
|
||||
* been acquired.
|
||||
*/
|
||||
void acquire();
|
||||
|
||||
/**
|
||||
* Releases the wake lock. This has no effect if the wake lock has already
|
||||
* been released.
|
||||
*/
|
||||
void release();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.briarproject.bramble.api.system;
|
||||
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface AndroidWakeLockManager {
|
||||
|
||||
/**
|
||||
* Creates a wake lock with the given tag. The tag is only used for
|
||||
* logging; the underlying OS wake lock will use its own tag.
|
||||
*/
|
||||
AndroidWakeLock createWakeLock(String tag);
|
||||
|
||||
/**
|
||||
* Runs the given task while holding a wake lock.
|
||||
*/
|
||||
void runWakefully(Runnable r, String tag);
|
||||
|
||||
/**
|
||||
* Submits the given task to the given executor while holding a wake lock.
|
||||
* The lock is released when the task completes, or if an exception is
|
||||
* thrown while submitting or running the task.
|
||||
*/
|
||||
void executeWakefully(Runnable r, Executor executor, String tag);
|
||||
|
||||
/**
|
||||
* Starts a dedicated thread to run the given task asynchronously. A wake
|
||||
* lock is acquired before starting the thread and released when the task
|
||||
* completes, or if an exception is thrown while starting the thread or
|
||||
* running the task.
|
||||
* <p>
|
||||
* This method should only be used for lifecycle management tasks that
|
||||
* can't be run on an executor.
|
||||
*/
|
||||
void executeWakefully(Runnable r, String tag);
|
||||
}
|
||||
@@ -118,11 +118,6 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
||||
try {
|
||||
NetworkInfo net = connectivityManager.getActiveNetworkInfo();
|
||||
boolean connected = net != null && net.isConnected();
|
||||
// Research into Android's behavior to check network connectivity
|
||||
// (https://code.briarproject.org/briar/public-mesh-research/-/issues/19)
|
||||
// has shown that NetworkInfo#isConnected() returns true if the device
|
||||
// is connected to any Wifi, independent of whether any specific IP
|
||||
// address can be reached using it or any domain names can be resolved.
|
||||
boolean wifi = false, ipv6Only = false;
|
||||
if (connected) {
|
||||
wifi = net.getType() == TYPE_WIFI;
|
||||
|
||||
@@ -2,10 +2,10 @@ package org.briarproject.bramble.plugin.bluetooth;
|
||||
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -55,7 +55,6 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission;
|
||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -98,11 +97,6 @@ class AndroidBluetoothPlugin extends
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isBluetoothAccessible() {
|
||||
return hasBtConnectPermission(app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws PluginException {
|
||||
super.start();
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.bluetooth;
|
||||
import android.app.Application;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
@@ -14,6 +13,7 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
@@ -2,11 +2,11 @@ package org.briarproject.bramble.plugin.bluetooth;
|
||||
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLock;
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.io.TimeoutMonitor;
|
||||
import org.briarproject.bramble.api.plugin.Plugin;
|
||||
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -33,10 +33,8 @@ class AndroidBluetoothTransportConnection
|
||||
super(plugin);
|
||||
this.connectionLimiter = connectionLimiter;
|
||||
this.socket = socket;
|
||||
InputStream socketIn = socket.getInputStream();
|
||||
if (socketIn == null) throw new IOException();
|
||||
in = timeoutMonitor.createTimeoutInputStream(socketIn,
|
||||
plugin.getMaxIdleTime() * 2L);
|
||||
in = timeoutMonitor.createTimeoutInputStream(
|
||||
socket.getInputStream(), plugin.getMaxIdleTime() * 2);
|
||||
wakeLock = wakeLockManager.createWakeLock("BluetoothConnection");
|
||||
wakeLock.acquire();
|
||||
String address = socket.getRemoteDevice().getAddress();
|
||||
@@ -50,9 +48,7 @@ class AndroidBluetoothTransportConnection
|
||||
|
||||
@Override
|
||||
protected OutputStream getOutputStream() throws IOException {
|
||||
OutputStream socketOut = socket.getOutputStream();
|
||||
if (socketOut == null) throw new IOException();
|
||||
return socketOut;
|
||||
return socket.getOutputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
package org.briarproject.bramble.plugin.tor;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
|
||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||
import org.briarproject.bramble.api.network.NetworkManager;
|
||||
import org.briarproject.bramble.api.plugin.Backoff;
|
||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import androidx.annotation.ChecksSdkIntAtLeast;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
class AndroidTorPlugin extends TorPlugin {
|
||||
|
||||
private static final List<String> LIBRARY_ARCHITECTURES =
|
||||
asList("armeabi-v7a", "arm64-v8a", "x86", "x86_64");
|
||||
|
||||
private static final String TOR_LIB_NAME = "libtor.so";
|
||||
private static final String OBFS4_LIB_NAME = "libobfs4proxy.so";
|
||||
private static final String SNOWFLAKE_LIB_NAME = "libsnowflake.so";
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(AndroidTorPlugin.class.getName());
|
||||
|
||||
private final Application app;
|
||||
private final AndroidWakeLock wakeLock;
|
||||
private final File torLib, obfs4Lib, snowflakeLib;
|
||||
|
||||
AndroidTorPlugin(Executor ioExecutor,
|
||||
Executor wakefulIoExecutor,
|
||||
Application app,
|
||||
NetworkManager networkManager,
|
||||
LocationUtils locationUtils,
|
||||
SocketFactory torSocketFactory,
|
||||
Clock clock,
|
||||
ResourceProvider resourceProvider,
|
||||
CircumventionProvider circumventionProvider,
|
||||
BatteryManager batteryManager,
|
||||
AndroidWakeLockManager wakeLockManager,
|
||||
Backoff backoff,
|
||||
TorRendezvousCrypto torRendezvousCrypto,
|
||||
PluginCallback callback,
|
||||
String architecture,
|
||||
long maxLatency,
|
||||
int maxIdleTime,
|
||||
File torDirectory,
|
||||
int torSocksPort,
|
||||
int torControlPort) {
|
||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||
torSocketFactory, clock, resourceProvider,
|
||||
circumventionProvider, batteryManager, backoff,
|
||||
torRendezvousCrypto, callback, architecture, maxLatency,
|
||||
maxIdleTime, torDirectory, torSocksPort, torControlPort);
|
||||
this.app = app;
|
||||
wakeLock = wakeLockManager.createWakeLock("TorPlugin");
|
||||
String nativeLibDir = app.getApplicationInfo().nativeLibraryDir;
|
||||
torLib = new File(nativeLibDir, TOR_LIB_NAME);
|
||||
obfs4Lib = new File(nativeLibDir, OBFS4_LIB_NAME);
|
||||
snowflakeLib = new File(nativeLibDir, SNOWFLAKE_LIB_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getProcessId() {
|
||||
return android.os.Process.myPid();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getLastUpdateTime() {
|
||||
try {
|
||||
PackageManager pm = app.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(app.getPackageName(), 0);
|
||||
return pi.lastUpdateTime;
|
||||
} catch (NameNotFoundException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enableNetwork(boolean enable) throws IOException {
|
||||
if (enable) wakeLock.acquire();
|
||||
super.enableNetwork(enable);
|
||||
if (!enable) wakeLock.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ChecksSdkIntAtLeast(api = 25)
|
||||
protected boolean canVerifyLetsEncryptCerts() {
|
||||
return SDK_INT >= 25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
wakeLock.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getTorExecutableFile() {
|
||||
return torLib.exists() ? torLib : super.getTorExecutableFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getObfs4ExecutableFile() {
|
||||
return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getSnowflakeExecutableFile() {
|
||||
return snowflakeLib.exists()
|
||||
? snowflakeLib : super.getSnowflakeExecutableFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installTorExecutable() throws IOException {
|
||||
installExecutable(super.getTorExecutableFile(), torLib, TOR_LIB_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installObfs4Executable() throws IOException {
|
||||
installExecutable(super.getObfs4ExecutableFile(), obfs4Lib,
|
||||
OBFS4_LIB_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installSnowflakeExecutable() throws IOException {
|
||||
installExecutable(super.getSnowflakeExecutableFile(), snowflakeLib,
|
||||
SNOWFLAKE_LIB_NAME);
|
||||
}
|
||||
|
||||
private void installExecutable(File extracted, File lib, String libName)
|
||||
throws IOException {
|
||||
if (lib.exists()) {
|
||||
// If an older version left behind a binary, delete it
|
||||
if (extracted.exists()) {
|
||||
if (extracted.delete()) LOG.info("Deleted old binary");
|
||||
else LOG.info("Failed to delete old binary");
|
||||
}
|
||||
} else if (SDK_INT < 29) {
|
||||
// The binary wasn't extracted at install time. Try to extract it
|
||||
extractLibraryFromApk(libName, extracted);
|
||||
} else {
|
||||
// No point extracting the binary, we won't be allowed to execute it
|
||||
throw new FileNotFoundException(lib.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private void extractLibraryFromApk(String libName, File dest)
|
||||
throws IOException {
|
||||
File sourceDir = new File(app.getApplicationInfo().sourceDir);
|
||||
if (sourceDir.isFile()) {
|
||||
// Look for other APK files in the same directory, if we're allowed
|
||||
File parent = sourceDir.getParentFile();
|
||||
if (parent != null) sourceDir = parent;
|
||||
}
|
||||
List<String> libPaths = getSupportedLibraryPaths(libName);
|
||||
for (File apk : findApkFiles(sourceDir)) {
|
||||
ZipInputStream zin = new ZipInputStream(new FileInputStream(apk));
|
||||
for (ZipEntry e = zin.getNextEntry(); e != null;
|
||||
e = zin.getNextEntry()) {
|
||||
if (libPaths.contains(e.getName())) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Extracting " + e.getName()
|
||||
+ " from " + apk.getAbsolutePath());
|
||||
}
|
||||
extract(zin, dest); // Zip input stream will be closed
|
||||
return;
|
||||
}
|
||||
}
|
||||
zin.close();
|
||||
}
|
||||
throw new FileNotFoundException(libName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all files with the extension .apk or .APK under the given root.
|
||||
*/
|
||||
private List<File> findApkFiles(File root) {
|
||||
List<File> files = new ArrayList<>();
|
||||
findApkFiles(root, files);
|
||||
return files;
|
||||
}
|
||||
|
||||
private void findApkFiles(File f, List<File> files) {
|
||||
if (f.isFile() && f.getName().toLowerCase().endsWith(".apk")) {
|
||||
files.add(f);
|
||||
} else if (f.isDirectory()) {
|
||||
File[] children = f.listFiles();
|
||||
if (children != null) {
|
||||
for (File child : children) findApkFiles(child, files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths at which libraries with the given name would be found
|
||||
* inside an APK file, for all architectures supported by the device, in
|
||||
* order of preference.
|
||||
*/
|
||||
private List<String> getSupportedLibraryPaths(String libName) {
|
||||
List<String> architectures = new ArrayList<>();
|
||||
for (String abi : AndroidUtils.getSupportedArchitectures()) {
|
||||
if (LIBRARY_ARCHITECTURES.contains(abi)) {
|
||||
architectures.add("lib/" + abi + "/" + libName);
|
||||
}
|
||||
}
|
||||
return architectures;
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,9 @@ package org.briarproject.bramble.plugin.tor;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.network.NetworkManager;
|
||||
import org.briarproject.bramble.api.plugin.Backoff;
|
||||
@@ -15,13 +13,12 @@ import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.onionwrapper.AndroidTorWrapper;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
import org.briarproject.onionwrapper.TorWrapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -31,7 +28,6 @@ import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.getSupportedArchitectures;
|
||||
|
||||
@Immutable
|
||||
@@ -43,13 +39,13 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
||||
|
||||
@Inject
|
||||
AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||
@EventExecutor Executor eventExecutor,
|
||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||
NetworkManager networkManager,
|
||||
LocationUtils locationUtils,
|
||||
EventBus eventBus,
|
||||
SocketFactory torSocketFactory,
|
||||
BackoffFactory backoffFactory,
|
||||
ResourceProvider resourceProvider,
|
||||
CircumventionProvider circumventionProvider,
|
||||
BatteryManager batteryManager,
|
||||
Clock clock,
|
||||
@@ -59,8 +55,8 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
||||
@TorControlPort int torControlPort,
|
||||
Application app,
|
||||
AndroidWakeLockManager wakeLockManager) {
|
||||
super(ioExecutor, eventExecutor, wakefulIoExecutor, networkManager,
|
||||
locationUtils, eventBus, torSocketFactory, backoffFactory,
|
||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||
eventBus, torSocketFactory, backoffFactory, resourceProvider,
|
||||
circumventionProvider, batteryManager, clock, crypto,
|
||||
torDirectory, torSocksPort, torControlPort);
|
||||
this.app = app;
|
||||
@@ -83,18 +79,12 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
||||
TorPlugin createPluginInstance(Backoff backoff,
|
||||
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
||||
String architecture) {
|
||||
TorWrapper tor = new AndroidTorWrapper(app, wakeLockManager,
|
||||
ioExecutor, eventExecutor, architecture, torDirectory,
|
||||
torSocksPort, torControlPort);
|
||||
// Android versions 7.1 and newer can verify Let's Encrypt TLS certs
|
||||
// signed with the IdentTrust DST Root X3 certificate. Older versions
|
||||
// of Android consider the certificate to have expired at the end of
|
||||
// September 2021.
|
||||
boolean canVerifyLetsEncryptCerts = SDK_INT >= 25;
|
||||
return new TorPlugin(ioExecutor, wakefulIoExecutor,
|
||||
networkManager, locationUtils, torSocketFactory,
|
||||
circumventionProvider, batteryManager, backoff,
|
||||
torRendezvousCrypto, tor, callback, MAX_LATENCY,
|
||||
MAX_IDLE_TIME, canVerifyLetsEncryptCerts);
|
||||
return new AndroidTorPlugin(ioExecutor,
|
||||
wakefulIoExecutor, app, networkManager, locationUtils,
|
||||
torSocketFactory, clock, resourceProvider,
|
||||
circumventionProvider, batteryManager, wakeLockManager,
|
||||
backoff, torRendezvousCrypto, callback, architecture,
|
||||
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
||||
torControlPort);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.os.StrictMode;
|
||||
import android.provider.Settings;
|
||||
@@ -14,17 +15,11 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.os.Build.FINGERPRINT;
|
||||
import static android.os.Build.SERIAL;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Process.myPid;
|
||||
import static android.os.Process.myTid;
|
||||
import static android.os.Process.myUid;
|
||||
import static android.provider.Settings.Secure.ANDROID_ID;
|
||||
|
||||
@Immutable
|
||||
@@ -44,27 +39,22 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
||||
@Override
|
||||
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
|
||||
super.writeToEntropyPool(out);
|
||||
out.writeInt(myPid());
|
||||
out.writeInt(myTid());
|
||||
out.writeInt(myUid());
|
||||
if (FINGERPRINT != null) out.writeUTF(FINGERPRINT);
|
||||
if (SERIAL != null) out.writeUTF(SERIAL);
|
||||
out.writeInt(android.os.Process.myPid());
|
||||
out.writeInt(android.os.Process.myTid());
|
||||
out.writeInt(android.os.Process.myUid());
|
||||
if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
|
||||
if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
|
||||
ContentResolver contentResolver = appContext.getContentResolver();
|
||||
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
||||
if (id != null) out.writeUTF(id);
|
||||
// On API 31 and higher we need permission to access bonded devices
|
||||
if (SDK_INT < 31) {
|
||||
Parcel parcel = Parcel.obtain();
|
||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bt != null) {
|
||||
@SuppressLint("MissingPermission")
|
||||
Set<BluetoothDevice> deviceSet = bt.getBondedDevices();
|
||||
for (BluetoothDevice device : deviceSet)
|
||||
parcel.writeParcelable(device, 0);
|
||||
}
|
||||
out.write(parcel.marshall());
|
||||
parcel.recycle();
|
||||
Parcel parcel = Parcel.obtain();
|
||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bt != null) {
|
||||
for (BluetoothDevice device : bt.getBondedDevices())
|
||||
parcel.writeParcelable(device, 0);
|
||||
}
|
||||
out.write(parcel.marshall());
|
||||
parcel.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,7 +77,7 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
||||
.invoke(null, (Object) seed);
|
||||
// Mix the output of the Linux PRNG into the OpenSSL PRNG
|
||||
int bytesRead = (Integer) Class.forName(
|
||||
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||
.getMethod("RAND_load_file", String.class, long.class)
|
||||
.invoke(null, "/dev/urandom", 1024);
|
||||
if (bytesRead != 1024) throw new IOException();
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.system;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
@@ -68,4 +69,11 @@ public class AndroidSystemModule {
|
||||
ResourceProvider provideResourceProvider(AndroidResourceProvider provider) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
AndroidWakeLockManager provideWakeLockManager(
|
||||
AndroidWakeLockManagerImpl wakeLockManager) {
|
||||
return wakeLockManager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import android.content.Intent;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
import org.briarproject.bramble.api.lifecycle.Service;
|
||||
import org.briarproject.bramble.api.system.AlarmListener;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
import org.briarproject.bramble.api.system.Wakeful;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
@@ -41,7 +41,6 @@ import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.system.AlarmConstants.EXTRA_PID;
|
||||
import static org.briarproject.bramble.system.AlarmConstants.REQUEST_ALARM;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.getImmutableFlags;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
@@ -200,7 +199,7 @@ class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener {
|
||||
Intent i = new Intent(app, AlarmReceiver.class);
|
||||
i.putExtra(EXTRA_PID, android.os.Process.myPid());
|
||||
return PendingIntent.getBroadcast(app, REQUEST_ALARM, i,
|
||||
getImmutableFlags(FLAG_CANCEL_CURRENT));
|
||||
FLAG_CANCEL_CURRENT);
|
||||
}
|
||||
|
||||
private class ScheduledTask
|
||||
|
||||
@@ -2,9 +2,9 @@ package org.briarproject.bramble.system;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.system.AlarmListener;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.logging.Level.FINE;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
/**
|
||||
* A wrapper around a {@link SharedWakeLock} that provides the more convenient
|
||||
* semantics of {@link AndroidWakeLock} (i.e. calls to acquire() and release()
|
||||
* don't need to be balanced).
|
||||
*/
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class AndroidWakeLockImpl implements AndroidWakeLock {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(AndroidWakeLockImpl.class.getName());
|
||||
|
||||
private static final AtomicInteger INSTANCE_ID = new AtomicInteger(0);
|
||||
|
||||
private final SharedWakeLock sharedWakeLock;
|
||||
private final String tag;
|
||||
|
||||
private final Object lock = new Object();
|
||||
@GuardedBy("lock")
|
||||
private boolean held = false;
|
||||
|
||||
AndroidWakeLockImpl(SharedWakeLock sharedWakeLock, String tag) {
|
||||
this.sharedWakeLock = sharedWakeLock;
|
||||
this.tag = tag + "_" + INSTANCE_ID.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acquire() {
|
||||
synchronized (lock) {
|
||||
if (held) {
|
||||
if (LOG.isLoggable(FINE)) {
|
||||
LOG.fine(tag + " already acquired");
|
||||
}
|
||||
} else {
|
||||
if (LOG.isLoggable(FINE)) {
|
||||
LOG.fine(tag + " acquiring shared wake lock");
|
||||
}
|
||||
held = true;
|
||||
sharedWakeLock.acquire();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
synchronized (lock) {
|
||||
if (held) {
|
||||
if (LOG.isLoggable(FINE)) {
|
||||
LOG.fine(tag + " releasing shared wake lock");
|
||||
}
|
||||
held = false;
|
||||
sharedWakeLock.release();
|
||||
} else {
|
||||
if (LOG.isLoggable(FINE)) {
|
||||
LOG.fine(tag + " already released");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.PowerManager;
|
||||
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.content.Context.POWER_SERVICE;
|
||||
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
||||
|
||||
@NotNullByDefault
|
||||
class AndroidWakeLockManagerImpl implements AndroidWakeLockManager {
|
||||
|
||||
/**
|
||||
* How often to replace the wake lock.
|
||||
*/
|
||||
private static final long LOCK_DURATION_MS = MINUTES.toMillis(1);
|
||||
|
||||
/**
|
||||
* Automatically release the lock this many milliseconds after it's due
|
||||
* to have been replaced and released.
|
||||
*/
|
||||
private static final long SAFETY_MARGIN_MS = SECONDS.toMillis(30);
|
||||
|
||||
private final SharedWakeLock sharedWakeLock;
|
||||
|
||||
@Inject
|
||||
AndroidWakeLockManagerImpl(Application app,
|
||||
ScheduledExecutorService scheduledExecutorService) {
|
||||
PowerManager powerManager = (PowerManager)
|
||||
requireNonNull(app.getSystemService(POWER_SERVICE));
|
||||
String tag = getWakeLockTag(app);
|
||||
sharedWakeLock = new RenewableWakeLock(powerManager,
|
||||
scheduledExecutorService, PARTIAL_WAKE_LOCK, tag,
|
||||
LOCK_DURATION_MS, SAFETY_MARGIN_MS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidWakeLock createWakeLock(String tag) {
|
||||
return new AndroidWakeLockImpl(sharedWakeLock, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runWakefully(Runnable r, String tag) {
|
||||
AndroidWakeLock wakeLock = createWakeLock(tag);
|
||||
wakeLock.acquire();
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
wakeLock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeWakefully(Runnable r, Executor executor, String tag) {
|
||||
AndroidWakeLock wakeLock = createWakeLock(tag);
|
||||
wakeLock.acquire();
|
||||
try {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
// Release the wake lock if the task throws an exception
|
||||
wakeLock.release();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
// Release the wake lock if the executor throws an exception when
|
||||
// we submit the task (in which case the release() call above won't
|
||||
// happen)
|
||||
wakeLock.release();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeWakefully(Runnable r, String tag) {
|
||||
AndroidWakeLock wakeLock = createWakeLock(tag);
|
||||
wakeLock.acquire();
|
||||
try {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
r.run();
|
||||
} finally {
|
||||
wakeLock.release();
|
||||
}
|
||||
}).start();
|
||||
} catch (Exception e) {
|
||||
wakeLock.release();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private String getWakeLockTag(Context ctx) {
|
||||
PackageManager pm = ctx.getPackageManager();
|
||||
if (isInstalled(pm, "com.huawei.powergenie")) {
|
||||
return "LocationManagerService";
|
||||
} else if (isInstalled(pm, "com.evenwell.PowerMonitor")) {
|
||||
return "AudioIn";
|
||||
}
|
||||
return ctx.getPackageName();
|
||||
}
|
||||
|
||||
private boolean isInstalled(PackageManager pm, String packageName) {
|
||||
try {
|
||||
pm.getPackageInfo(packageName, 0);
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.logging.Level.FINE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class RenewableWakeLock implements SharedWakeLock {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(RenewableWakeLock.class.getName());
|
||||
|
||||
private final PowerManager powerManager;
|
||||
private final ScheduledExecutorService scheduledExecutorService;
|
||||
private final int levelAndFlags;
|
||||
private final String tag;
|
||||
private final long durationMs, safetyMarginMs;
|
||||
|
||||
private final Object lock = new Object();
|
||||
@GuardedBy("lock")
|
||||
@Nullable
|
||||
private WakeLock wakeLock;
|
||||
@GuardedBy("lock")
|
||||
@Nullable
|
||||
private Future<?> future;
|
||||
@GuardedBy("lock")
|
||||
private int refCount = 0;
|
||||
@GuardedBy("lock")
|
||||
private long acquired = 0;
|
||||
|
||||
RenewableWakeLock(PowerManager powerManager,
|
||||
ScheduledExecutorService scheduledExecutorService,
|
||||
int levelAndFlags,
|
||||
String tag,
|
||||
long durationMs,
|
||||
long safetyMarginMs) {
|
||||
this.powerManager = powerManager;
|
||||
this.scheduledExecutorService = scheduledExecutorService;
|
||||
this.levelAndFlags = levelAndFlags;
|
||||
this.tag = tag;
|
||||
this.durationMs = durationMs;
|
||||
this.safetyMarginMs = safetyMarginMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acquire() {
|
||||
synchronized (lock) {
|
||||
refCount++;
|
||||
if (refCount == 1) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Acquiring wake lock " + tag);
|
||||
}
|
||||
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
|
||||
// We do our own reference counting so we can replace the lock
|
||||
// TODO: Check whether using a ref-counted wake lock affects
|
||||
// power management apps
|
||||
wakeLock.setReferenceCounted(false);
|
||||
wakeLock.acquire(durationMs + safetyMarginMs);
|
||||
future = scheduledExecutorService.schedule(this::renew,
|
||||
durationMs, MILLISECONDS);
|
||||
acquired = android.os.SystemClock.elapsedRealtime();
|
||||
} else if (LOG.isLoggable(FINE)) {
|
||||
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renew() {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Renewing wake lock " + tag);
|
||||
synchronized (lock) {
|
||||
if (wakeLock == null) {
|
||||
LOG.info("Already released");
|
||||
return;
|
||||
}
|
||||
if (LOG.isLoggable(FINE)) {
|
||||
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
|
||||
}
|
||||
long now = android.os.SystemClock.elapsedRealtime();
|
||||
long expiry = acquired + durationMs + safetyMarginMs;
|
||||
if (now > expiry && LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Wake lock expired " + (now - expiry) + " ms ago");
|
||||
}
|
||||
WakeLock oldWakeLock = wakeLock;
|
||||
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
|
||||
wakeLock.setReferenceCounted(false);
|
||||
wakeLock.acquire(durationMs + safetyMarginMs);
|
||||
oldWakeLock.release();
|
||||
future = scheduledExecutorService.schedule(this::renew, durationMs,
|
||||
MILLISECONDS);
|
||||
acquired = now;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
synchronized (lock) {
|
||||
refCount--;
|
||||
if (refCount == 0) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Releasing wake lock " + tag);
|
||||
}
|
||||
requireNonNull(future).cancel(false);
|
||||
future = null;
|
||||
requireNonNull(wakeLock).release();
|
||||
wakeLock = null;
|
||||
acquired = 0;
|
||||
} else if (LOG.isLoggable(FINE)) {
|
||||
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
interface SharedWakeLock {
|
||||
|
||||
/**
|
||||
* Acquires the wake lock. This increments the wake lock's reference count,
|
||||
* so unlike {@link AndroidWakeLock#acquire()} every call to this method
|
||||
* must be followed by a balancing call to {@link #release()}.
|
||||
*/
|
||||
void acquire();
|
||||
|
||||
/**
|
||||
* Releases the wake lock. This decrements the wake lock's reference count,
|
||||
* so unlike {@link AndroidWakeLock#release()} every call to this method
|
||||
* must follow a balancing call to {@link #acquire()}.
|
||||
*/
|
||||
void release();
|
||||
}
|
||||
@@ -22,13 +22,8 @@ import java.util.Scanner;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.Manifest.permission.BLUETOOTH_CONNECT;
|
||||
import static android.app.PendingIntent.FLAG_IMMUTABLE;
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Process.myPid;
|
||||
import static android.os.Process.myUid;
|
||||
import static java.lang.Runtime.getRuntime;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
||||
@@ -53,11 +48,6 @@ public class AndroidUtils {
|
||||
return abis;
|
||||
}
|
||||
|
||||
public static boolean hasBtConnectPermission(Context ctx) {
|
||||
return SDK_INT < 31 || ctx.checkPermission(BLUETOOTH_CONNECT, myPid(),
|
||||
myUid()) == PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
public static String getBluetoothAddress(Context ctx,
|
||||
BluetoothAdapter adapter) {
|
||||
return getBluetoothAddressAndMethod(ctx, adapter).getFirst();
|
||||
@@ -65,9 +55,6 @@ public class AndroidUtils {
|
||||
|
||||
public static Pair<String, String> getBluetoothAddressAndMethod(Context ctx,
|
||||
BluetoothAdapter adapter) {
|
||||
// If we don't have permission to access the adapter's address, let
|
||||
// the caller know we can't find it
|
||||
if (!hasBtConnectPermission(ctx)) return new Pair<>("", "");
|
||||
// Return the adapter's address if it's valid and not fake
|
||||
@SuppressLint("HardwareIds")
|
||||
String address = adapter.getAddress();
|
||||
@@ -152,11 +139,4 @@ public class AndroidUtils {
|
||||
public static boolean isUiThread() {
|
||||
return Looper.myLooper() == Looper.getMainLooper();
|
||||
}
|
||||
|
||||
public static int getImmutableFlags(int flags) {
|
||||
if (SDK_INT >= 23) {
|
||||
return FLAG_IMMUTABLE | flags;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,64 +2,164 @@ dependencyVerification {
|
||||
verify = [
|
||||
'androidx.annotation:annotation:1.5.0:annotation-1.5.0.jar:261fb7c0210858500bab66d34354972a75166ab4182add283780b05513d6ec4a',
|
||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||
'com.android.tools.analytics-library:protos:30.0.3:protos-30.0.3.jar:f62b89dcd9de719c6a7b7e15fb1dd20e45b57222e675cf633607bd0ed6bca7e7',
|
||||
'com.android.tools.analytics-library:shared:30.0.3:shared-30.0.3.jar:05aa9ba3cc890354108521fdf99802565aae5dd6ca44a6ac8bb8d594d1c1cd15',
|
||||
'com.android.tools.analytics-library:tracker:30.0.3:tracker-30.0.3.jar:5d0ef35bf6733e96210b5085a2a202152921bf834d345959dce1ca3369b528df',
|
||||
'com.android.tools.build:aapt2-proto:4.1.0-alpha01-6193524:aapt2-proto-4.1.0-alpha01-6193524.jar:17e75523e1e92dd4f222c7368ee41df9e964a508232f591e265d0c499baf9dca',
|
||||
'com.android.tools.build:apksig:7.0.3:apksig-7.0.3.jar:012337a2803c9a30dfc41dcbc6450686ee9e5f582549f7f126479f743a343ec9',
|
||||
'com.android.tools.build:apkzlib:7.0.3:apkzlib-7.0.3.jar:b31e53174c92db83c5cc6e7dac6734ea4e907a72e452c2bf1818dfd082c59397',
|
||||
'com.android.tools.build:builder-model:7.0.3:builder-model-7.0.3.jar:483f99d7494a5bed027e1e8d29111384cf535d4842f0be5a79805bd44bb68d4e',
|
||||
'com.android.tools.build:builder-test-api:7.0.3:builder-test-api-7.0.3.jar:f6de4bc2cef545e8367bf82d7c733829c7be3b0b3b8b09fd8c58f2150e59ab46',
|
||||
'com.android.tools.build:builder:7.0.3:builder-7.0.3.jar:c6952da0094b094c2ba0fe84c675622097c5d9b9f9beb53485b860320540cf1d',
|
||||
'com.android.tools.build:manifest-merger:30.0.3:manifest-merger-30.0.3.jar:72b346ba6318b4b6260e6e49df4bea5da2e12329ab6c2beb2269c49a9f51f178',
|
||||
'com.android.tools.ddms:ddmlib:30.0.3:ddmlib-30.0.3.jar:7a914a68ab93393657297234e2f37b22410ae9a433cba692ce8c727c9607e3bb',
|
||||
'com.android.tools.external.com-intellij:intellij-core:30.0.3:intellij-core-30.0.3.jar:1ebe858d3f58eeaa8c06507f8ac0f1c7051e6c61f35a70f3c3967d5734d3abc5',
|
||||
'com.android.tools.external.com-intellij:kotlin-compiler:30.0.3:kotlin-compiler-30.0.3.jar:ed00e441f427cb4e0d418287b9da30b12b7f735f9af32e6b5d3dc960b6a742fc',
|
||||
'com.android.tools.external.org-jetbrains:uast:30.0.3:uast-30.0.3.jar:a77801bee6ff509910e459525c9c34d7f04b066ade123547f16f1917548eadea',
|
||||
'com.android.tools.layoutlib:layoutlib-api:30.0.3:layoutlib-api-30.0.3.jar:4caa87e9ca2e11315f650d576cd59fec1793373bc3fca3f6d53c029e7534e7c4',
|
||||
'com.android.tools.lint:lint-api:30.0.3:lint-api-30.0.3.jar:bcecbd2f752a6560096a9029a47d1de6bd788a51bab505c5ebfba6a18524b983',
|
||||
'com.android.tools.lint:lint-checks:30.0.3:lint-checks-30.0.3.jar:25a7cd42dc3ad502337f131fb8b7e873c53301db0a67b1c64dd4ae7a8eb66cec',
|
||||
'com.android.tools.lint:lint-gradle:30.0.3:lint-gradle-30.0.3.jar:94544d6147a809bf2fd3440e51f28a4e42e547d74aab53eefd74938cdad42c26',
|
||||
'com.android.tools.lint:lint-model:30.0.3:lint-model-30.0.3.jar:0b940a7f575c2ff5cbd038260f41dde686a93c672213881ead3ce8af3513b396',
|
||||
'com.android.tools.lint:lint:30.0.3:lint-30.0.3.jar:ee4f11001e0c7e3b776e0d67399ad354b19b0f168822ec2b7db47c0910ed227d',
|
||||
'com.android.tools:annotations:30.0.3:annotations-30.0.3.jar:5c1944982fda8555855c4f5422fabf0dc8e2306e1f5460e9ad82dae71316bc31',
|
||||
'com.android.tools:common:30.0.3:common-30.0.3.jar:8751efaaf2c2ddd1f0a37526c794347def6a3057ca9fc510307c13a6cf0d036f',
|
||||
'com.android.tools:dvlib:30.0.3:dvlib-30.0.3.jar:5affafcec390041e5afd64cb924153f5e474db47ee8ccc2f555b495083141233',
|
||||
'com.android.tools:repository:30.0.3:repository-30.0.3.jar:0a40c6f16c506903ce2c609affd8228aceda73a69d93dfa42d4f02b8491449f6',
|
||||
'com.android.tools:sdk-common:30.0.3:sdk-common-30.0.3.jar:b45570a380360236ffee0f6bb593d66b673bad3834dfe0d6c9871fa7188ee0eb',
|
||||
'com.android.tools:sdklib:30.0.3:sdklib-30.0.3.jar:7088f20a414fab170a21e457825e14ebe099f753558e02c8acc12c67eb412162',
|
||||
'com.android:signflinger:7.0.3:signflinger-7.0.3.jar:903a4536db3e96b4e1e1dc1e400eb0b91bf7866d9b39cd7ec94d75dde158f152',
|
||||
'com.android:zipflinger:7.0.3:zipflinger-7.0.3.jar:fd209c960a3eff7a339e6fcba07d5e9ef4604d1633c69ab2df987460d9804140',
|
||||
'com.beust:jcommander:1.78:jcommander-1.78.jar:7891debb84b5f83e9bd57593ebece3399abbe0fd938cf306b3534c57913b9615',
|
||||
'com.github.javaparser:javaparser-core:3.17.0:javaparser-core-3.17.0.jar:23f5c982e1c7771423d37d52c774e8d2e80fd7ea7305ebe448797a96f67e6fca',
|
||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.dagger:dagger-compiler:2.43.2:dagger-compiler-2.43.2.jar:298c020ee6ed2f4cc651ebbfdb7f8de329b07c44b618d65be117846a850e2a03',
|
||||
'com.google.dagger:dagger-producers:2.43.2:dagger-producers-2.43.2.jar:e7f5d9ffc85d48a49c8e22e02833d418f7ccad5d7512f529964db5127ab915ff',
|
||||
'com.google.dagger:dagger-spi:2.43.2:dagger-spi-2.43.2.jar:3bae8d9dadeaaa5927da6f094389a560c12c05fec3d2711d2fa79292c7a7d7ad',
|
||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
||||
'com.google.devtools.ksp:symbol-processing-api:1.7.0-1.0.6:symbol-processing-api-1.7.0-1.0.6.jar:adc29417be5ca9ff42118105fea4e36d9ef44987abfc41432309371a60198941',
|
||||
'com.google.errorprone:error_prone_annotations:2.7.1:error_prone_annotations-2.7.1.jar:cd5257c08a246cf8628817ae71cb822be192ef91f6881ca4a3fcff4f1de1cff3',
|
||||
'com.google.code.gson:gson:2.8.6:gson-2.8.6.jar:c8fb4839054d280b3033f800d1f5a97de2f028eb8ba2eb458ad287e536f3f25f',
|
||||
'com.google.dagger:dagger-compiler:2.33:dagger-compiler-2.33.jar:aa8a0d8370c578fd6999802d0d90b9829377a46d2c1141e11b8f737970e7155e',
|
||||
'com.google.dagger:dagger-producers:2.33:dagger-producers-2.33.jar:5897f0b6eef799c2adfe3ccacc58c0fb374d58acb063c3ebe5366c38a8bce5c8',
|
||||
'com.google.dagger:dagger-spi:2.33:dagger-spi-2.33.jar:e2dcab2221b8afb9556ef0a1c83b0bd5f42552e254322a257330f754cdbbb9d4',
|
||||
'com.google.dagger:dagger:2.33:dagger-2.33.jar:d8798c5b8cf6b125234e33af5c6293bb9f2208ce29b57924c35b8c0be7b6bdcb',
|
||||
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a',
|
||||
'com.google.errorprone:error_prone_annotations:2.3.4:error_prone_annotations-2.3.4.jar:baf7d6ea97ce606c53e11b6854ba5f2ce7ef5c24dddf0afa18d1260bd25b002c',
|
||||
'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.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26',
|
||||
'com.google.guava:guava:31.0.1-jre:guava-31.0.1-jre.jar:d5be94d65e87bd219fb3193ad1517baa55a3b88fc91d21cf735826ab5af087b9',
|
||||
'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
|
||||
'com.google.guava:guava:30.1-jre:guava-30.1-jre.jar:e6dd072f9d3fe02a4600688380bd422bdac184caf6fe2418cfdd0934f09432aa',
|
||||
'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.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
|
||||
'com.google.protobuf:protobuf-java:3.10.0:protobuf-java-3.10.0.jar:161d7d61a8cb3970891c299578702fd079646e032329d6c2cabf998d191437c9',
|
||||
'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
|
||||
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||
'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
|
||||
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
|
||||
'com.sun.istack:istack-commons-runtime:3.0.8:istack-commons-runtime-3.0.8.jar:4ffabb06be454a05e4398e20c77fa2b6308d4b88dfbef7ca30a76b5b7d5505ef',
|
||||
'com.sun.xml.fastinfoset:FastInfoset:1.2.16:FastInfoset-1.2.16.jar:056f3a1e144409f21ed16afc26805f58e9a21f3fce1543c42d400719d250c511',
|
||||
'com.thoughtworks.qdox:qdox:1.12.1:qdox-1.12.1.jar:21fba22f830e9268f07cf4ab2d99e8181abbdcb0cb91ee0228eb3cb918dcdd1d',
|
||||
'commons-codec:commons-codec:1.10:commons-codec-1.10.jar:4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569',
|
||||
'commons-io:commons-io:2.4:commons-io-2.4.jar:cc6a41dc3eaacc9e440a6bd0d2890b20d36b4ee408fe2d67122f328bb6e01581',
|
||||
'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
|
||||
'info.picocli:picocli:4.5.2:picocli-4.5.2.jar:b4395e9a67932616efd2245d984bf5fcd453c2c5049558c3ce959ac2af4d3fac',
|
||||
'it.unimi.dsi:fastutil:8.4.0:fastutil-8.4.0.jar:2ad2824a4a0a0eb836b52ee2fc84ba2134f44bce7bfa54015ae3f31c710a3071',
|
||||
'jakarta.activation:jakarta.activation-api:1.2.1:jakarta.activation-api-1.2.1.jar:8b0a0f52fa8b05c5431921a063ed866efaa41dadf2e3a7ee3e1961f2b0d9645b',
|
||||
'jakarta.xml.bind:jakarta.xml.bind-api:2.3.2:jakarta.xml.bind-api-2.3.2.jar:69156304079bdeed9fc0ae3b39389f19b3cc4ba4443bc80508995394ead742ea',
|
||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'jline:jline:2.14.6:jline-2.14.6.jar:97d1acaac82409be42e622d7a54d3ae9d08517e8aefdea3d2ba9791150c2f02d',
|
||||
'junit:junit:4.13.1:junit-4.13.1.jar:c30719db974d6452793fe191b3638a5777005485bae145924044530ffa5f6122',
|
||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||
'net.java.dev.jna:jna-platform:5.6.0:jna-platform-5.6.0.jar:9ecea8bf2b1b39963939d18b70464eef60c508fed8820f9dcaba0c35518eabf7',
|
||||
'net.java.dev.jna:jna:5.6.0:jna-5.6.0.jar:5557e235a8aa2f9766d5dc609d67948f2a8832c2d796cea9ef1d6cbe0b3b7eaf',
|
||||
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||
'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.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
|
||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||
'org.briarproject:dont-kill-me-lib:0.2.6:dont-kill-me-lib-0.2.6.aar:8a4cc201143227c0865c2edfba035f71109bf02e1ab26444fa3e42d3c569960f',
|
||||
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
||||
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||
'org.briarproject:obfs4proxy-android:0.0.14-tor2:obfs4proxy-android-0.0.14-tor2.jar:a0a93770d6760ce57d9dbd31cc7177687374e00c3361dac22ab75e3b6e0f289e',
|
||||
'org.briarproject:onionwrapper-android:0.0.1:onionwrapper-android-0.0.1.aar:959115946586daa090f057645cf75992407a59025e221c3bf88d2aa930ef3919',
|
||||
'org.briarproject:onionwrapper-core:0.0.1:onionwrapper-core-0.0.1.jar:a1937506b00ee6620e909a500e5d004be81f94a6f7d7c898e1a9e841a8ae8a2a',
|
||||
'org.briarproject:snowflake-android:2.5.1:snowflake-android-2.5.1.jar:88ec81c17b1b6fa884d06839dec0330e328b45c89f88c970a213ce91ca8eac87',
|
||||
'org.briarproject:tor-android:0.4.7.13-2:tor-android-0.4.7.13-2.jar:453fd463b234a2104edd7f0d02d0649cbb5c5efbe47a76df3828f55a3f90f8b5',
|
||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
||||
'org.apache.ant:ant-antlr:1.10.9:ant-antlr-1.10.9.jar:7623dc9d0f20ea713290c6bf1a23f4c059447aef7ff9f5b2be75960f3f028d2e',
|
||||
'org.apache.ant:ant-junit:1.10.9:ant-junit-1.10.9.jar:960bdc8827954d62206ba42d0a68a7ee4476175ba47bb113e17e77cce7394630',
|
||||
'org.apache.ant:ant-launcher:1.10.9:ant-launcher-1.10.9.jar:fcce891f57f3be72149ff96ac2a80574165b3e0839866b95d24528f3027d50c1',
|
||||
'org.apache.ant:ant:1.10.9:ant-1.10.9.jar:0715478af585ea80a18985613ebecdc7922122d45b2c3c970ff9b352cddb75fc',
|
||||
'org.apache.commons:commons-compress:1.20:commons-compress-1.20.jar:0aeb625c948c697ea7b205156e112363b59ed5e2551212cd4e460bdb72c7c06e',
|
||||
'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7',
|
||||
'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd',
|
||||
'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e',
|
||||
'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.briarproject:obfs4proxy-android:0.0.14:obfs4proxy-android-0.0.14.jar:ad9b1ee4757b05867a19e993147bbb018bddd1f26ce3da746d5f037d5991a8c8',
|
||||
'org.briarproject:snowflake-android:2.3.1:snowflake-android-2.3.1.jar:1f83c9a070f87b7074af13627709a8b5aced5460104be7166af736b1bb73c293',
|
||||
'org.briarproject:tor-android:0.4.5.14:tor-android-0.4.5.14.jar:7cf1beaa6c1db51fc8fac263aba9624ef289c3db29772509efcbc59f7057330a',
|
||||
'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:3.5.0:checker-qual-3.5.0.jar:729990b3f18a95606fc2573836b6958bcdb44cb52bfbd1b7aa9c339cff35a5a4',
|
||||
'org.codehaus.groovy:groovy-ant:3.0.7:groovy-ant-3.0.7.jar:6ed2ba82813d128f7050c24142e87b3dc2ad8b504786280eb03e81f0cf6a5793',
|
||||
'org.codehaus.groovy:groovy-astbuilder:3.0.7:groovy-astbuilder-3.0.7.jar:b290451eb1583666e906c41f7d14747b4cc96363c99c478b244634fd5dfc9013',
|
||||
'org.codehaus.groovy:groovy-cli-picocli:3.0.7:groovy-cli-picocli-3.0.7.jar:71b4bd11fb30a9c7b5618e22122c9c5141958fb27f4dcf0068b6f715088f6916',
|
||||
'org.codehaus.groovy:groovy-console:3.0.7:groovy-console-3.0.7.jar:0541b358b6b8e5363215026736168fccfec1d91bac678d066fa77349eeeaa5dd',
|
||||
'org.codehaus.groovy:groovy-datetime:3.0.7:groovy-datetime-3.0.7.jar:b9823d14b1a4f94236ae2f8a471701aab17e093e1b33402b91550b5c8dd88f04',
|
||||
'org.codehaus.groovy:groovy-docgenerator:3.0.7:groovy-docgenerator-3.0.7.jar:bf53f7a11c9eb1e278e1b8ed2714c741bcf781235c803ad3ba1555f2614573f3',
|
||||
'org.codehaus.groovy:groovy-groovydoc:3.0.7:groovy-groovydoc-3.0.7.jar:86b24dfc23c005066ab83927cdb54177f06c9531773f2e2d2ecc9a131f7c2677',
|
||||
'org.codehaus.groovy:groovy-groovysh:3.0.7:groovy-groovysh-3.0.7.jar:5c40e78cbc09726aedd1c75fab112d245d665d6294870f9119e6cd3013ed14ab',
|
||||
'org.codehaus.groovy:groovy-jmx:3.0.7:groovy-jmx-3.0.7.jar:0a89f3007884eb156751937d93382038b83d39c7c2f0ab156ebf251a7251f2ab',
|
||||
'org.codehaus.groovy:groovy-json:3.0.7:groovy-json-3.0.7.jar:df1f0ee475e3fc93a6a0d17548294e160cca5de6d9d36817a7be1fbe650de03b',
|
||||
'org.codehaus.groovy:groovy-jsr223:3.0.7:groovy-jsr223-3.0.7.jar:1dbd969595332416193baa660fbb45743d19696eaa25fe98e591a2739e13517e',
|
||||
'org.codehaus.groovy:groovy-macro:3.0.7:groovy-macro-3.0.7.jar:c6cc06df526b39e2c359e2435f0071594c5a1c7babafaa6c184fdd8fa931531f',
|
||||
'org.codehaus.groovy:groovy-nio:3.0.7:groovy-nio-3.0.7.jar:db54c577882b294cd8c975ec5451596441baf54781319c61627dca0e0c2361ef',
|
||||
'org.codehaus.groovy:groovy-servlet:3.0.7:groovy-servlet-3.0.7.jar:5b6a909bf501c209adfb6205b9e740649609074455fd979bf9da4853e6ff9a39',
|
||||
'org.codehaus.groovy:groovy-sql:3.0.7:groovy-sql-3.0.7.jar:252bb6c74e1a9f41756ad4fbd3b0d2eddc93bb61109961dd1952a37bf2d57a64',
|
||||
'org.codehaus.groovy:groovy-swing:3.0.7:groovy-swing-3.0.7.jar:bd942032d9328d54c6679c49a41f6caa0d4a0039ebe598493b8a647730d98cff',
|
||||
'org.codehaus.groovy:groovy-templates:3.0.7:groovy-templates-3.0.7.jar:f119e07f650ef186ae5a4b944f9e30915b14311bad47c94a6b32de8d4f69bc80',
|
||||
'org.codehaus.groovy:groovy-test-junit5:3.0.7:groovy-test-junit5-3.0.7.jar:c16eeea07b8e396891e266d7ba9388b24ac804237ffdd9a792b0d08969bad014',
|
||||
'org.codehaus.groovy:groovy-test:3.0.7:groovy-test-3.0.7.jar:f71afd7c25d43017f89ea47e6de6daec971d159047dae083c1513a8422d44b90',
|
||||
'org.codehaus.groovy:groovy-testng:3.0.7:groovy-testng-3.0.7.jar:713d5f2231bbb5712aefd362151b9ffd884aeb7ef2e773315cc54259cbdd063d',
|
||||
'org.codehaus.groovy:groovy-xml:3.0.7:groovy-xml-3.0.7.jar:8a62e7c9ddece3e82676c4bef2f2c100f459602cd1fb6a14e94187bf863e97ff',
|
||||
'org.codehaus.groovy:groovy:3.0.7:groovy-3.0.7.jar:51d1777e8dd1f00e60ea56e00d8a354ff5aab1f00fc8464ae8d39d71867e401f',
|
||||
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
||||
'org.glassfish.jaxb:jaxb-runtime:2.3.2:jaxb-runtime-2.3.2.jar:e6e0a1e89fb6ff786279e6a0082d5cef52dc2ebe67053d041800737652b4fd1b',
|
||||
'org.glassfish.jaxb:txw2:2.3.2:txw2-2.3.2.jar:4a6a9f483388d461b81aa9a28c685b8b74c0597993bf1884b04eddbca95f48fe',
|
||||
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
||||
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
||||
'org.jacoco:org.jacoco.agent:0.8.7:org.jacoco.agent-0.8.7.jar:9cbcc986e0fbe821a78ff1f8f7d5216f200e5eb124e7f6837d1dc4a77b28b143',
|
||||
'org.jacoco:org.jacoco.ant:0.8.7:org.jacoco.ant-0.8.7.jar:97ca96a382c3f23a44d8eb4c4e6c3742a30cb8005774a76ced0fc4806ce49605',
|
||||
'org.jacoco:org.jacoco.core:0.8.7:org.jacoco.core-0.8.7.jar:ad7739b5fb5969aa1a8aead3d74ed54dc82ed012f1f10f336bd1b96e71c1a13c',
|
||||
'org.jacoco:org.jacoco.report:0.8.7:org.jacoco.report-0.8.7.jar:cc89258623700a6c932592153cb528785876b6da183d5431f97efbba6f020e5b',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.8.0:kotlin-stdlib-common-1.8.0.jar:78ef93b59e603cc0fe51def9bd4c037b07cbace3b3b7806d1a490a42bc1f4cb2',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0:kotlin-stdlib-jdk7-1.8.0.jar:4c889d1d9803f5f2eb6c1592a6b7e62369ac7660c9eee15aba16fec059163666',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0:kotlin-stdlib-jdk8-1.7.0.jar:cf058e11db1dfc9944680c8c61b95ac689aaaa8a3eb30bced028100f038f030b',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.7.0:kotlin-stdlib-1.7.0.jar:aa88e9625577957f3249a46cb6e166ee09b369e600f7a11d148d16b0a6d87f05',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.8.0:kotlin-stdlib-1.8.0.jar:c77bef8774640b9fb9d6e217459ff220dae59878beb7d2e4b430506feffc654e',
|
||||
'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.5.0:kotlinx-metadata-jvm-0.5.0.jar:ca063a96639b08b9eaa0de4d65e899480740a6efbe28ab9a8681a2ced03055a4',
|
||||
'org.jacoco:org.jacoco.agent:0.8.3:org.jacoco.agent-0.8.3.jar:522deb254ee16a04cc8341cc8f335f5cb7232982994d961b9cf3a0454709209f',
|
||||
'org.jacoco:org.jacoco.ant:0.8.3:org.jacoco.ant-0.8.3.jar:735844e1ae15f9b875b42a27ac5cb61cc26e106d9e839e5d1c6756709b424ce0',
|
||||
'org.jacoco:org.jacoco.core:0.8.3:org.jacoco.core-0.8.3.jar:0818437bc060a0c7cc798148f22b713702aae2771aba104444407697d578f1ea',
|
||||
'org.jacoco:org.jacoco.report:0.8.3:org.jacoco.report-0.8.3.jar:aae08fa4ff043c807b8876cdb2d8705eb8449a55efce461baa6c09da245088c1',
|
||||
'org.jetbrains.intellij.deps:trove4j:1.0.20181211:trove4j-1.0.20181211.jar:affb7c85a3c87bdcf69ff1dbb84de11f63dc931293934bc08cd7ab18de083601',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.4.32:kotlin-reflect-1.4.32.jar:dbf19e9cdaa9c3c170f3f6f6ce3922f38dfc1d7fa1cab5b7c23a19da8b5eec5b',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20:kotlin-stdlib-common-1.4.20.jar:a7112c9b3cefee418286c9c9372f7af992bd1e6e030691d52f60cb36dbec8320',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32:kotlin-stdlib-common-1.4.32.jar:e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10:kotlin-stdlib-common-1.7.10.jar:19f102efe9629f8eabc63853ad15c533e47c47f91fca09285c5bde86e59f91d4',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.32:kotlin-stdlib-jdk7-1.4.32.jar:5f801e75ca27d8791c14b07943c608da27620d910a8093022af57f543d5d98b6',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32:kotlin-stdlib-jdk8-1.4.32.jar:adc43e54757b106e0cd7b3b7aa257dff471b61efdabe067fc02b2f57e2396262',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.4.20:kotlin-stdlib-1.4.20.jar:b8ab1da5cdc89cb084d41e1f28f20a42bd431538642a5741c52bbfae3fa3e656',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.4.32:kotlin-stdlib-1.4.32.jar:13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.7.10:kotlin-stdlib-1.7.10.jar:e771fe74250a943e8f6346713201ff1d8cb95c3a5d1a91a22b65a9e04f6a8901',
|
||||
'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0:kotlinx-metadata-jvm-0.1.0.jar:9753bb39efef35957c5c15df9a3cb769aabf2cdfa74b47afcb7760e5146be3b5',
|
||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
|
||||
'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b',
|
||||
'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd',
|
||||
'org.jmock:jmock-testjar:2.12.0:jmock-testjar-2.12.0.jar:efefbcf6cd294d0e29f0c46eb2a3380d4ca4e1763ff719c69e2f2ac62f564a04',
|
||||
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
|
||||
'org.junit.jupiter:junit-jupiter-api:5.7.0:junit-jupiter-api-5.7.0.jar:b03f78e0daeed2d77a0af9bcd662b4cdb9693f7ee72e01a539b508b84c63d182',
|
||||
'org.junit.jupiter:junit-jupiter-engine:5.7.0:junit-jupiter-engine-5.7.0.jar:dfa26af94644ac2612dde6625852fcb550a0d21caa243257de54cba738ba87af',
|
||||
'org.junit.platform:junit-platform-commons:1.7.0:junit-platform-commons-1.7.0.jar:5330ee87cc7586e6e25175a34e9251624ff12ff525269d3415d0b4ca519b6fea',
|
||||
'org.junit.platform:junit-platform-engine:1.7.0:junit-platform-engine-1.7.0.jar:75f21a20dc594afdc875736725b408cec6d0344874d29f34b2dd3075500236f2',
|
||||
'org.junit.platform:junit-platform-launcher:1.7.0:junit-platform-launcher-1.7.0.jar:fbdc748fde4c4279fe1d3c607447cb3b7ccd45d7338fc574f8a894ddf2d16818',
|
||||
'org.jvnet.staxex:stax-ex:1.8.1:stax-ex-1.8.1.jar:20522549056e9e50aa35ef0b445a2e47a53d06be0b0a9467d704e2483ffb049a',
|
||||
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
|
||||
'org.ow2.asm:asm-analysis:9.1:asm-analysis-9.1.jar:81a88041b1b8beda5a8a99646098046c48709538270c49def68abff25ac3be34',
|
||||
'org.ow2.asm:asm-commons:9.1:asm-commons-9.1.jar:afcb26dc1fc12c0c4a99ada670908dd82e18dfc488caf5ee92546996b470c00c',
|
||||
'org.ow2.asm:asm-tree:9.1:asm-tree-9.1.jar:fd00afa49e9595d7646205b09cecb4a776a8ff0ba06f2d59b8f7bf9c704b4a73',
|
||||
'org.opentest4j:opentest4j:1.2.0:opentest4j-1.2.0.jar:58812de60898d976fb81ef3b62da05c6604c18fd4a249f5044282479fc286af2',
|
||||
'org.ow2.asm:asm-analysis:7.0:asm-analysis-7.0.jar:e981f8f650c4d900bb033650b18e122fa6b161eadd5f88978d08751f72ee8474',
|
||||
'org.ow2.asm:asm-commons:7.0:asm-commons-7.0.jar:fed348ef05958e3e846a3ac074a12af5f7936ef3d21ce44a62c4fa08a771927d',
|
||||
'org.ow2.asm:asm-tree:7.0:asm-tree-7.0.jar:cfd7a0874f9de36a999c127feeadfbfe6e04d4a71ee954d7af3d853f0be48a6c',
|
||||
'org.ow2.asm:asm-util:7.0:asm-util-7.0.jar:75fbbca440ef463f41c2b0ab1a80abe67e910ac486da60a7863cbcb5bae7e145',
|
||||
'org.ow2.asm:asm:7.0:asm-7.0.jar:b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf',
|
||||
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
||||
'org.ow2.asm:asm:9.1:asm-9.1.jar:cda4de455fab48ff0bcb7c48b4639447d4de859a7afc30a094a986f0936beba2',
|
||||
'org.testng:testng:7.3.0:testng-7.3.0.jar:63727488f9717d57f0d0a0fee5a1fc10a2be9cfcff2ec3a7187656d663c0774e',
|
||||
'xerces:xercesImpl:2.12.0:xercesImpl-2.12.0.jar:b50d3a4ca502faa4d1c838acb8aa9480446953421f7327e338c5dda3da5e76d0',
|
||||
'xml-apis:xml-apis:1.4.01:xml-apis-1.4.01.jar:a840968176645684bb01aed376e067ab39614885f9eee44abe35a5f20ebe7fad',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,10 +8,9 @@ apply from: 'witness.gradle'
|
||||
|
||||
dependencies {
|
||||
api 'org.briarproject:null-safety:0.1'
|
||||
api 'com.google.code.findbugs:jsr305:3.0.2'
|
||||
api 'javax.inject:javax.inject:1'
|
||||
api "com.google.dagger:dagger:$dagger_version"
|
||||
|
||||
implementation "com.google.dagger:dagger:$dagger_version"
|
||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
|
||||
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
@@ -26,7 +25,7 @@ configurations {
|
||||
testOutput.extendsFrom(testCompile)
|
||||
}
|
||||
task jarTest(type: Jar, dependsOn: testClasses) {
|
||||
from sourceSets.test.output, sourceSets.main.output
|
||||
from sourceSets.test.output
|
||||
classifier = 'test'
|
||||
}
|
||||
artifacts {
|
||||
|
||||
@@ -11,6 +11,8 @@ public interface FeatureFlags {
|
||||
|
||||
boolean shouldEnableDisappearingMessages();
|
||||
|
||||
boolean shouldEnableMailbox();
|
||||
|
||||
boolean shouldEnablePrivateGroupsInCore();
|
||||
|
||||
boolean shouldEnableForumsInCore();
|
||||
|
||||
@@ -16,7 +16,6 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
||||
|
||||
@Immutable
|
||||
@@ -24,24 +23,17 @@ import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOC
|
||||
public abstract class BdfMessageValidator implements MessageValidator {
|
||||
|
||||
protected static final Logger LOG =
|
||||
getLogger(BdfMessageValidator.class.getName());
|
||||
Logger.getLogger(BdfMessageValidator.class.getName());
|
||||
|
||||
protected final ClientHelper clientHelper;
|
||||
protected final MetadataEncoder metadataEncoder;
|
||||
protected final Clock clock;
|
||||
protected final boolean canonical;
|
||||
|
||||
protected BdfMessageValidator(ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock, boolean canonical) {
|
||||
this.clientHelper = clientHelper;
|
||||
this.metadataEncoder = metadataEncoder;
|
||||
this.clock = clock;
|
||||
this.canonical = canonical;
|
||||
}
|
||||
|
||||
protected BdfMessageValidator(ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
this(clientHelper, metadataEncoder, clock, true);
|
||||
this.clientHelper = clientHelper;
|
||||
this.metadataEncoder = metadataEncoder;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
protected abstract BdfMessageContext validateMessage(Message m, Group g,
|
||||
@@ -57,7 +49,7 @@ public abstract class BdfMessageValidator implements MessageValidator {
|
||||
"Timestamp is too far in the future");
|
||||
}
|
||||
try {
|
||||
BdfList bodyList = clientHelper.toList(m, canonical);
|
||||
BdfList bodyList = clientHelper.toList(m.getBody());
|
||||
BdfMessageContext result = validateMessage(m, g, bodyList);
|
||||
Metadata meta = metadataEncoder.encode(result.getDictionary());
|
||||
return new MessageContext(meta, result.getDependencies());
|
||||
|
||||
@@ -49,9 +49,6 @@ public interface ClientHelper {
|
||||
BdfList getMessageAsList(Transaction txn, MessageId m) throws DbException,
|
||||
FormatException;
|
||||
|
||||
BdfList getMessageAsList(Transaction txn, MessageId m, boolean canonical)
|
||||
throws DbException, FormatException;
|
||||
|
||||
BdfDictionary getGroupMetadataAsDictionary(GroupId g) throws DbException,
|
||||
FormatException;
|
||||
|
||||
@@ -109,8 +106,6 @@ public interface ClientHelper {
|
||||
|
||||
BdfList toList(Message m) throws FormatException;
|
||||
|
||||
BdfList toList(Message m, boolean canonical) throws FormatException;
|
||||
|
||||
BdfList toList(Author a);
|
||||
|
||||
byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
|
||||
|
||||
@@ -11,7 +11,7 @@ import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
@NotThreadSafe
|
||||
public final class BdfDictionary extends TreeMap<String, Object> {
|
||||
public class BdfDictionary extends TreeMap<String, Object> {
|
||||
|
||||
public static final Object NULL_VALUE = new Object();
|
||||
|
||||
@@ -39,9 +39,9 @@ public final class BdfDictionary extends TreeMap<String, Object> {
|
||||
}
|
||||
|
||||
public Boolean getBoolean(String key) throws FormatException {
|
||||
Boolean value = getOptionalBoolean(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
Object o = get(key);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -52,16 +52,19 @@ public final class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Boolean getBoolean(String key, Boolean defaultValue)
|
||||
throws FormatException {
|
||||
Boolean value = getOptionalBoolean(key);
|
||||
return value == null ? defaultValue : value;
|
||||
public Boolean getBoolean(String key, Boolean defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public Long getLong(String key) throws FormatException {
|
||||
Long value = getOptionalLong(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
Object o = get(key);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
||||
if (o instanceof Short) return ((Short) o).longValue();
|
||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -75,37 +78,20 @@ public final class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Long getLong(String key, Long defaultValue) throws FormatException {
|
||||
Long value = getOptionalLong(key);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Integer getInt(String key) throws FormatException {
|
||||
Integer value = getOptionalInt(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getOptionalInt(String key) throws FormatException {
|
||||
Long value = getOptionalLong(key);
|
||||
if (value == null) return null;
|
||||
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
|
||||
throw new FormatException();
|
||||
}
|
||||
return value.intValue();
|
||||
}
|
||||
|
||||
public Integer getInt(String key, Integer defaultValue)
|
||||
throws FormatException {
|
||||
Integer value = getOptionalInt(key);
|
||||
return value == null ? defaultValue : value;
|
||||
public Long getLong(String key, Long defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
||||
if (o instanceof Short) return ((Short) o).longValue();
|
||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public Double getDouble(String key) throws FormatException {
|
||||
Double value = getOptionalDouble(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
Object o = get(key);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -117,16 +103,17 @@ public final class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Double getDouble(String key, Double defaultValue)
|
||||
throws FormatException {
|
||||
Double value = getOptionalDouble(key);
|
||||
return value == null ? defaultValue : value;
|
||||
public Double getDouble(String key, Double defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public String getString(String key) throws FormatException {
|
||||
String value = getOptionalString(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
Object o = get(key);
|
||||
if (o instanceof String) return (String) o;
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -137,16 +124,17 @@ public final class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public String getString(String key, String defaultValue)
|
||||
throws FormatException {
|
||||
String value = getOptionalString(key);
|
||||
return value == null ? defaultValue : value;
|
||||
public String getString(String key, String defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof String) return (String) o;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public byte[] getRaw(String key) throws FormatException {
|
||||
byte[] value = getOptionalRaw(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
Object o = get(key);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -158,16 +146,17 @@ public final class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public byte[] getRaw(String key, byte[] defaultValue)
|
||||
throws FormatException {
|
||||
byte[] value = getOptionalRaw(key);
|
||||
return value == null ? defaultValue : value;
|
||||
public byte[] getRaw(String key, byte[] defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public BdfList getList(String key) throws FormatException {
|
||||
BdfList value = getOptionalList(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -178,16 +167,16 @@ public final class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfList getList(String key, BdfList defaultValue)
|
||||
throws FormatException {
|
||||
BdfList value = getOptionalList(key);
|
||||
return value == null ? defaultValue : value;
|
||||
public BdfList getList(String key, BdfList defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(String key) throws FormatException {
|
||||
BdfDictionary value = getOptionalDictionary(key);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -199,9 +188,9 @@ public final class BdfDictionary extends TreeMap<String, Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(String key, BdfDictionary defaultValue)
|
||||
throws FormatException {
|
||||
BdfDictionary value = getOptionalDictionary(key);
|
||||
return value == null ? defaultValue : value;
|
||||
public BdfDictionary getDictionary(String key, BdfDictionary defaultValue) {
|
||||
Object o = get(key);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import javax.annotation.concurrent.NotThreadSafe;
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
|
||||
@NotThreadSafe
|
||||
public final class BdfList extends ArrayList<Object> {
|
||||
public class BdfList extends ArrayList<Object> {
|
||||
|
||||
/**
|
||||
* Factory method for constructing lists inline.
|
||||
@@ -33,15 +33,15 @@ public final class BdfList extends ArrayList<Object> {
|
||||
super(items);
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean isInRange(int index) {
|
||||
return index >= 0 && index < size();
|
||||
}
|
||||
|
||||
public Boolean getBoolean(int index) throws FormatException {
|
||||
Boolean value = getOptionalBoolean(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -53,16 +53,21 @@ public final class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Boolean getBoolean(int index, Boolean defaultValue)
|
||||
throws FormatException {
|
||||
Boolean value = getOptionalBoolean(index);
|
||||
return value == null ? defaultValue : value;
|
||||
public Boolean getBoolean(int index, Boolean defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof Boolean) return (Boolean) o;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public Long getLong(int index) throws FormatException {
|
||||
Long value = getOptionalLong(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
||||
if (o instanceof Short) return ((Short) o).longValue();
|
||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -77,37 +82,22 @@ public final class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Long getLong(int index, Long defaultValue) throws FormatException {
|
||||
Long value = getOptionalLong(index);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public Integer getInt(int index) throws FormatException {
|
||||
Integer value = getOptionalInt(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getOptionalInt(int index) throws FormatException {
|
||||
Long value = getOptionalLong(index);
|
||||
if (value == null) return null;
|
||||
if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
|
||||
throw new FormatException();
|
||||
}
|
||||
return value.intValue();
|
||||
}
|
||||
|
||||
public Integer getInt(int index, Integer defaultValue)
|
||||
throws FormatException {
|
||||
Integer value = getOptionalInt(index);
|
||||
return value == null ? defaultValue : value;
|
||||
public Long getLong(int index, Long defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof Long) return (Long) o;
|
||||
if (o instanceof Integer) return ((Integer) o).longValue();
|
||||
if (o instanceof Short) return ((Short) o).longValue();
|
||||
if (o instanceof Byte) return ((Byte) o).longValue();
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public Double getDouble(int index) throws FormatException {
|
||||
Double value = getOptionalDouble(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -120,16 +110,19 @@ public final class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public Double getDouble(int index, Double defaultValue)
|
||||
throws FormatException {
|
||||
Double value = getOptionalDouble(index);
|
||||
return value == null ? defaultValue : value;
|
||||
public Double getDouble(int index, Double defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof Double) return (Double) o;
|
||||
if (o instanceof Float) return ((Float) o).doubleValue();
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public String getString(int index) throws FormatException {
|
||||
String value = getOptionalString(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof String) return (String) o;
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -141,16 +134,19 @@ public final class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public String getString(int index, String defaultValue)
|
||||
throws FormatException {
|
||||
String value = getOptionalString(index);
|
||||
return value == null ? defaultValue : value;
|
||||
public String getString(int index, String defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof String) return (String) o;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public byte[] getRaw(int index) throws FormatException {
|
||||
byte[] value = getOptionalRaw(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -163,16 +159,19 @@ public final class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public byte[] getRaw(int index, byte[] defaultValue)
|
||||
throws FormatException {
|
||||
byte[] value = getOptionalRaw(index);
|
||||
return value == null ? defaultValue : value;
|
||||
public byte[] getRaw(int index, byte[] defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof byte[]) return (byte[]) o;
|
||||
if (o instanceof Bytes) return ((Bytes) o).getBytes();
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public BdfList getList(int index) throws FormatException {
|
||||
BdfList value = getOptionalList(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -184,16 +183,18 @@ public final class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfList getList(int index, BdfList defaultValue)
|
||||
throws FormatException {
|
||||
BdfList value = getOptionalList(index);
|
||||
return value == null ? defaultValue : value;
|
||||
public BdfList getList(int index, BdfList defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfList) return (BdfList) o;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(int index) throws FormatException {
|
||||
BdfDictionary value = getOptionalDictionary(index);
|
||||
if (value == null) throw new FormatException();
|
||||
return value;
|
||||
if (!isInRange(index)) throw new FormatException();
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -206,9 +207,10 @@ public final class BdfList extends ArrayList<Object> {
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
public BdfDictionary getDictionary(int index, BdfDictionary defaultValue)
|
||||
throws FormatException {
|
||||
BdfDictionary value = getOptionalDictionary(index);
|
||||
return value == null ? defaultValue : value;
|
||||
public BdfDictionary getDictionary(int index, BdfDictionary defaultValue) {
|
||||
if (!isInRange(index)) return defaultValue;
|
||||
Object o = get(index);
|
||||
if (o instanceof BdfDictionary) return (BdfDictionary) o;
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,12 +32,6 @@ public interface BdfReader {
|
||||
|
||||
void skipLong() throws IOException;
|
||||
|
||||
boolean hasInt() throws IOException;
|
||||
|
||||
int readInt() throws IOException;
|
||||
|
||||
void skipInt() throws IOException;
|
||||
|
||||
boolean hasDouble() throws IOException;
|
||||
|
||||
double readDouble() throws IOException;
|
||||
@@ -60,11 +54,23 @@ public interface BdfReader {
|
||||
|
||||
BdfList readList() throws IOException;
|
||||
|
||||
void readListStart() throws IOException;
|
||||
|
||||
boolean hasListEnd() throws IOException;
|
||||
|
||||
void readListEnd() throws IOException;
|
||||
|
||||
void skipList() throws IOException;
|
||||
|
||||
boolean hasDictionary() throws IOException;
|
||||
|
||||
BdfDictionary readDictionary() throws IOException;
|
||||
|
||||
void readDictionaryStart() throws IOException;
|
||||
|
||||
boolean hasDictionaryEnd() throws IOException;
|
||||
|
||||
void readDictionaryEnd() throws IOException;
|
||||
|
||||
void skipDictionary() throws IOException;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ public interface BdfReaderFactory {
|
||||
|
||||
BdfReader createReader(InputStream in);
|
||||
|
||||
BdfReader createReader(InputStream in, boolean canonical);
|
||||
|
||||
BdfReader createReader(InputStream in, int nestedLimit,
|
||||
int maxBufferSize, boolean canonical);
|
||||
int maxBufferSize);
|
||||
}
|
||||
|
||||
@@ -24,5 +24,13 @@ public interface BdfWriter {
|
||||
|
||||
void writeList(Collection<?> c) throws IOException;
|
||||
|
||||
void writeListStart() throws IOException;
|
||||
|
||||
void writeListEnd() throws IOException;
|
||||
|
||||
void writeDictionary(Map<?, ?> m) throws IOException;
|
||||
|
||||
void writeDictionaryStart() throws IOException;
|
||||
|
||||
void writeDictionaryEnd() throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,18 @@
|
||||
package org.briarproject.bramble.api.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.mailbox.MailboxConstants;
|
||||
|
||||
public interface KeyAgreementConstants {
|
||||
|
||||
/**
|
||||
* The version of the BQP protocol used in beta releases. This version
|
||||
* number is reserved.
|
||||
*/
|
||||
byte BETA_PROTOCOL_VERSION = 89;
|
||||
|
||||
/**
|
||||
* The current version of the BQP protocol.
|
||||
*/
|
||||
byte PROTOCOL_VERSION = 4;
|
||||
|
||||
/**
|
||||
* The QR code format identifier, used to distinguish BQP QR codes from
|
||||
* QR codes used for other purposes. See
|
||||
* {@link MailboxConstants#QR_FORMAT_ID}.
|
||||
*/
|
||||
byte QR_FORMAT_ID = 0;
|
||||
|
||||
/**
|
||||
* The QR code format version.
|
||||
*/
|
||||
byte QR_FORMAT_VERSION = PROTOCOL_VERSION;
|
||||
|
||||
/**
|
||||
* The length of the BQP key commitment in bytes.
|
||||
*/
|
||||
|
||||
@@ -7,5 +7,5 @@ import java.io.IOException;
|
||||
@NotNullByDefault
|
||||
public interface PayloadParser {
|
||||
|
||||
Payload parse(String payload) throws IOException;
|
||||
Payload parse(byte[] raw) throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.bramble.api.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
|
||||
import java.util.List;
|
||||
@@ -20,18 +19,6 @@ public interface MailboxConstants {
|
||||
*/
|
||||
TransportId ID = new TransportId("org.briarproject.bramble.mailbox");
|
||||
|
||||
/**
|
||||
* The QR code format identifier, used to distinguish mailbox QR codes
|
||||
* from QR codes used for other purposes. See
|
||||
* {@link KeyAgreementConstants#QR_FORMAT_ID};
|
||||
*/
|
||||
byte QR_FORMAT_ID = 1;
|
||||
|
||||
/**
|
||||
* The QR code format version.
|
||||
*/
|
||||
byte QR_FORMAT_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Mailbox API versions that we support as a client. This is reported to our
|
||||
* contacts by {@link MailboxUpdateManager}.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.bramble.api.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
@@ -35,16 +34,6 @@ public interface MailboxManager {
|
||||
*/
|
||||
MailboxPairingTask startPairingTask(String qrCodePayload);
|
||||
|
||||
/**
|
||||
* Takes a textual QR code representation in
|
||||
* {@link org.briarproject.bramble.util.Base32} format and converts it
|
||||
* into a qrCodePayload as expected by {@link #startPairingTask(String)}.
|
||||
*
|
||||
* @throws FormatException when the provided payload did not include a
|
||||
* proper briar-mailbox:// link.
|
||||
*/
|
||||
String convertBase32Payload(String base32Payload) throws FormatException;
|
||||
|
||||
/**
|
||||
* Can be used by the UI to test the mailbox connection.
|
||||
*
|
||||
|
||||
@@ -1,44 +1,17 @@
|
||||
package org.briarproject.bramble.api.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
|
||||
public abstract class MailboxPairingState {
|
||||
|
||||
public abstract static class Pending extends MailboxPairingState {
|
||||
|
||||
public final long timeStarted;
|
||||
|
||||
private Pending(long timeStarted) {
|
||||
this.timeStarted = timeStarted;
|
||||
}
|
||||
public static class QrCodeReceived extends MailboxPairingState {
|
||||
}
|
||||
|
||||
public static class QrCodeReceived extends Pending {
|
||||
|
||||
public QrCodeReceived(long timeStarted) {
|
||||
super(timeStarted);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Pairing extends Pending {
|
||||
|
||||
public Pairing(long timeStarted) {
|
||||
super(timeStarted);
|
||||
}
|
||||
public static class Pairing extends MailboxPairingState {
|
||||
}
|
||||
|
||||
public static class Paired extends MailboxPairingState {
|
||||
}
|
||||
|
||||
public static class InvalidQrCode extends MailboxPairingState {
|
||||
|
||||
public final QrCodeType qrCodeType;
|
||||
public final int formatVersion;
|
||||
|
||||
public InvalidQrCode(QrCodeType qrCodeType, int formatVersion) {
|
||||
this.qrCodeType = qrCodeType;
|
||||
this.formatVersion = formatVersion;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MailboxAlreadyPaired extends MailboxPairingState {
|
||||
|
||||
@@ -27,6 +27,8 @@ public interface TorConstants {
|
||||
int PREF_TOR_NETWORK_AUTOMATIC = 0;
|
||||
int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1;
|
||||
int PREF_TOR_NETWORK_WITH_BRIDGES = 2;
|
||||
// TODO: Remove when settings migration code is removed
|
||||
int PREF_TOR_NETWORK_NEVER = 3;
|
||||
|
||||
// Default values for local settings
|
||||
boolean DEFAULT_PREF_PLUGIN_ENABLE = true;
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.briarproject.bramble.api.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface QrCodeClassifier {
|
||||
|
||||
enum QrCodeType {
|
||||
BQP,
|
||||
MAILBOX,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
Pair<QrCodeType, Integer> classifyQrCode(String payload);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.briarproject.bramble.api.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Thrown when a QR code that has been scanned does not have the expected type.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class WrongQrCodeTypeException extends FormatException {
|
||||
|
||||
private final QrCodeType qrCodeType;
|
||||
|
||||
public WrongQrCodeTypeException(QrCodeType qrCodeType) {
|
||||
this.qrCodeType = qrCodeType;
|
||||
}
|
||||
|
||||
public QrCodeType getQrCodeType() {
|
||||
return qrCodeType;
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static org.briarproject.bramble.util.StringUtils.startsWithIgnoreCase;
|
||||
|
||||
@NotNullByDefault
|
||||
public class OsUtils {
|
||||
|
||||
@@ -15,15 +13,15 @@ public class OsUtils {
|
||||
private static final String vendor = System.getProperty("java.vendor");
|
||||
|
||||
public static boolean isWindows() {
|
||||
return os != null && startsWithIgnoreCase(os, "Win");
|
||||
return os != null && os.contains("Windows");
|
||||
}
|
||||
|
||||
public static boolean isMac() {
|
||||
return os != null && os.equalsIgnoreCase("Mac OS X");
|
||||
return os != null && os.contains("Mac OS");
|
||||
}
|
||||
|
||||
public static boolean isLinux() {
|
||||
return os != null && startsWithIgnoreCase(os, "Linux") && !isAndroid();
|
||||
return os != null && os.contains("Linux") && !isAndroid();
|
||||
}
|
||||
|
||||
public static boolean isAndroid() {
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.util;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -14,21 +15,15 @@ import java.util.regex.Pattern;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static java.nio.charset.CodingErrorAction.IGNORE;
|
||||
import static java.nio.charset.CodingErrorAction.REPORT;
|
||||
import static java.util.regex.Pattern.CASE_INSENSITIVE;
|
||||
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed")
|
||||
@NotNullByDefault
|
||||
public class StringUtils {
|
||||
|
||||
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
public static final Charset US_ASCII = Charset.forName("US-ASCII");
|
||||
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||
|
||||
private static final Pattern MAC =
|
||||
Pattern.compile("[0-9a-f]{2}:[0-9a-f]{2}:" +
|
||||
"[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}",
|
||||
CASE_INSENSITIVE);
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
private static Pattern MAC = Pattern.compile("[0-9a-f]{2}:[0-9a-f]{2}:" +
|
||||
"[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}",
|
||||
CASE_INSENSITIVE);
|
||||
|
||||
private static final char[] HEX = new char[] {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
@@ -50,41 +45,33 @@ public class StringUtils {
|
||||
}
|
||||
|
||||
public static byte[] toUtf8(String s) {
|
||||
return s.getBytes(UTF_8);
|
||||
try {
|
||||
return s.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String fromUtf8(byte[] bytes) throws FormatException {
|
||||
return fromUtf8(bytes, 0, bytes.length, true);
|
||||
public static String fromUtf8(byte[] bytes) {
|
||||
return fromUtf8(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
public static String fromUtf8(byte[] bytes, int off, int len)
|
||||
throws FormatException {
|
||||
return fromUtf8(bytes, off, len, true);
|
||||
}
|
||||
|
||||
private static String fromUtf8(byte[] bytes, int off, int len,
|
||||
boolean strict) throws FormatException {
|
||||
public static String fromUtf8(byte[] bytes, int off, int len) {
|
||||
CharsetDecoder decoder = UTF_8.newDecoder();
|
||||
decoder.onMalformedInput(strict ? REPORT : IGNORE);
|
||||
decoder.onUnmappableCharacter(strict ? REPORT : IGNORE);
|
||||
decoder.onMalformedInput(IGNORE);
|
||||
decoder.onUnmappableCharacter(IGNORE);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes, off, len);
|
||||
try {
|
||||
return decoder.decode(buffer).toString();
|
||||
} catch (CharacterCodingException e) {
|
||||
throw new FormatException();
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String truncateUtf8(String s, int maxUtf8Length) {
|
||||
byte[] utf8 = toUtf8(s);
|
||||
if (utf8.length <= maxUtf8Length) return s;
|
||||
// Don't be strict when converting back, so that if we truncate a
|
||||
// multi-byte character the whole character gets dropped
|
||||
try {
|
||||
return fromUtf8(utf8, 0, maxUtf8Length, false);
|
||||
} catch (FormatException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
return fromUtf8(utf8, 0, maxUtf8Length);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,9 +163,4 @@ public class StringUtils {
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
// see https://stackoverflow.com/a/38947571
|
||||
static boolean startsWithIgnoreCase(String s, String prefix) {
|
||||
return s.regionMatches(true, 0, prefix, 0, prefix.length());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.bramble.api.data;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -9,12 +8,9 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class BdfDictionaryTest extends BrambleTestCase {
|
||||
@@ -23,20 +19,20 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
public void testConstructors() {
|
||||
assertEquals(Collections.<String, Object>emptyMap(),
|
||||
new BdfDictionary());
|
||||
assertEquals(singletonMap("foo", NULL_VALUE),
|
||||
new BdfDictionary(singletonMap("foo", NULL_VALUE)));
|
||||
assertEquals(Collections.singletonMap("foo", NULL_VALUE),
|
||||
new BdfDictionary(Collections.singletonMap("foo", NULL_VALUE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryMethod() {
|
||||
assertEquals(Collections.<String, Object>emptyMap(),
|
||||
BdfDictionary.of());
|
||||
assertEquals(singletonMap("foo", NULL_VALUE),
|
||||
assertEquals(Collections.singletonMap("foo", NULL_VALUE),
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongPromotion() throws Exception {
|
||||
public void testIntegerPromotion() throws Exception {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("foo", (byte) 1);
|
||||
d.put("bar", (short) 2);
|
||||
@@ -48,33 +44,6 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
assertEquals(Long.valueOf(4), d.getLong("bam"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntPromotionAndDemotion() throws Exception {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("foo", (byte) 1);
|
||||
d.put("bar", (short) 2);
|
||||
d.put("baz", 3);
|
||||
d.put("bam", 4L);
|
||||
assertEquals(Integer.valueOf(1), d.getInt("foo"));
|
||||
assertEquals(Integer.valueOf(2), d.getInt("bar"));
|
||||
assertEquals(Integer.valueOf(3), d.getInt("baz"));
|
||||
assertEquals(Integer.valueOf(4), d.getInt("bam"));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testIntUnderflow() throws Exception {
|
||||
BdfDictionary d =
|
||||
BdfDictionary.of(new BdfEntry("foo", Integer.MIN_VALUE - 1L));
|
||||
d.getInt("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testIntOverflow() throws Exception {
|
||||
BdfDictionary d =
|
||||
BdfDictionary.of(new BdfEntry("foo", Integer.MAX_VALUE + 1L));
|
||||
d.getInt("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatPromotion() throws Exception {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
@@ -98,7 +67,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeySetIteratorIsOrderedByKeys() {
|
||||
public void testKeySetIteratorIsOrderedByKeys() throws Exception {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("a", 1);
|
||||
d.put("d", 4);
|
||||
@@ -117,7 +86,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValuesIteratorIsOrderedByKeys() {
|
||||
public void testValuesIteratorIsOrderedByKeys() throws Exception {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("a", 1);
|
||||
d.put("d", 4);
|
||||
@@ -136,7 +105,7 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEntrySetIteratorIsOrderedByKeys() {
|
||||
public void testEntrySetIteratorIsOrderedByKeys() throws Exception {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put("a", 1);
|
||||
d.put("d", 4);
|
||||
@@ -161,284 +130,4 @@ public class BdfDictionaryTest extends BrambleTestCase {
|
||||
assertEquals("d", e.getKey());
|
||||
assertEquals(4, e.getValue());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getBoolean("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForLongThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getLong("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForIntThrowsFormatException() throws Exception {
|
||||
new BdfDictionary().getInt("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getDouble("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForStringThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getString("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForRawThrowsFormatException() throws Exception {
|
||||
new BdfDictionary().getRaw("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForListThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getList("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testMissingValueForDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfDictionary().getDictionary("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getBoolean("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForLongThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getLong("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForIntThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getInt("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForDoubleThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getDouble("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForStringThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getString("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForRawThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getRaw("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForListThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getList("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", NULL_VALUE)).getDictionary("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalMethodsReturnNullForMissingValue()
|
||||
throws Exception {
|
||||
testOptionalMethodsReturnNull(new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalMethodsReturnNullForNullValue() throws Exception {
|
||||
BdfDictionary d = BdfDictionary.of(new BdfEntry("foo", NULL_VALUE));
|
||||
testOptionalMethodsReturnNull(d);
|
||||
}
|
||||
|
||||
private void testOptionalMethodsReturnNull(BdfDictionary d)
|
||||
throws Exception {
|
||||
assertNull(d.getOptionalBoolean("foo"));
|
||||
assertNull(d.getOptionalLong("foo"));
|
||||
assertNull(d.getOptionalInt("foo"));
|
||||
assertNull(d.getOptionalDouble("foo"));
|
||||
assertNull(d.getOptionalString("foo"));
|
||||
assertNull(d.getOptionalRaw("foo"));
|
||||
assertNull(d.getOptionalList("foo"));
|
||||
assertNull(d.getOptionalDictionary("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultMethodsReturnDefaultForMissingValue()
|
||||
throws Exception {
|
||||
testDefaultMethodsReturnDefault(new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultMethodsReturnDefaultForNullValue() throws Exception {
|
||||
BdfDictionary d = BdfDictionary.of(new BdfEntry("foo", NULL_VALUE));
|
||||
testDefaultMethodsReturnDefault(d);
|
||||
}
|
||||
|
||||
private void testDefaultMethodsReturnDefault(BdfDictionary d)
|
||||
throws Exception {
|
||||
assertEquals(TRUE, d.getBoolean("foo", TRUE));
|
||||
assertEquals(Long.valueOf(123L), d.getLong("foo", 123L));
|
||||
assertEquals(Integer.valueOf(123), d.getInt("foo", 123));
|
||||
assertEquals(Double.valueOf(123D), d.getDouble("foo", 123D));
|
||||
assertEquals("123", d.getString("foo", "123"));
|
||||
byte[] defaultRaw = {1, 2, 3};
|
||||
assertArrayEquals(defaultRaw, d.getRaw("foo", defaultRaw));
|
||||
BdfList defaultList = BdfList.of(1, 2, 3);
|
||||
assertEquals(defaultList, d.getList("foo", defaultList));
|
||||
BdfDictionary defaultDict = BdfDictionary.of(new BdfEntry("123", 123));
|
||||
assertEquals(defaultDict, d.getDictionary("foo", defaultDict));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getBoolean("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalBoolean("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getBoolean("foo", true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForLongThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getLong("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalLongThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getOptionalLong("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultLongThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getLong("foo", 1L);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForIntThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getInt("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalIntThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getOptionalInt("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultIntThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 1.23)).getInt("foo", 1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDoubleThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getDouble("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalDouble("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getDouble("foo", 1D);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForStringThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getString("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalStringThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalString("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultStringThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getString("foo", "");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForRawThrowsFormatException() throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getRaw("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalRawThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalRaw("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultRawThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getRaw("foo", new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForListThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getList("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalListThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalList("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultListThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getList("foo",
|
||||
new BdfList());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getDictionary("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getOptionalDictionary("foo");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfDictionary.of(new BdfEntry("foo", 123)).getDictionary("foo",
|
||||
new BdfDictionary());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,31 +5,31 @@ import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class BdfListTest extends BrambleTestCase {
|
||||
|
||||
@Test
|
||||
public void testConstructors() {
|
||||
assertEquals(emptyList(), new BdfList());
|
||||
assertEquals(asList(1, 2, NULL_VALUE),
|
||||
new BdfList(asList(1, 2, NULL_VALUE)));
|
||||
assertEquals(Collections.emptyList(), new BdfList());
|
||||
assertEquals(Arrays.asList(1, 2, NULL_VALUE),
|
||||
new BdfList(Arrays.asList(1, 2, NULL_VALUE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryMethod() {
|
||||
assertEquals(emptyList(), BdfList.of());
|
||||
assertEquals(asList(1, 2, NULL_VALUE), BdfList.of(1, 2, NULL_VALUE));
|
||||
assertEquals(Collections.emptyList(), BdfList.of());
|
||||
assertEquals(Arrays.asList(1, 2, NULL_VALUE),
|
||||
BdfList.of(1, 2, NULL_VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongPromotion() throws Exception {
|
||||
public void testIntegerPromotion() throws Exception {
|
||||
BdfList list = new BdfList();
|
||||
list.add((byte) 1);
|
||||
list.add((short) 2);
|
||||
@@ -41,31 +41,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
assertEquals(Long.valueOf(4), list.getLong(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntPromotionAndDemotion() throws Exception {
|
||||
BdfList list = new BdfList();
|
||||
list.add((byte) 1);
|
||||
list.add((short) 2);
|
||||
list.add(3);
|
||||
list.add(4L);
|
||||
assertEquals(Integer.valueOf(1), list.getInt(0));
|
||||
assertEquals(Integer.valueOf(2), list.getInt(1));
|
||||
assertEquals(Integer.valueOf(3), list.getInt(2));
|
||||
assertEquals(Integer.valueOf(4), list.getInt(3));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testIntUnderflow() throws Exception {
|
||||
BdfList list = BdfList.of(Integer.MIN_VALUE - 1L);
|
||||
list.getInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testIntOverflow() throws Exception {
|
||||
BdfList list = BdfList.of(Integer.MAX_VALUE + 1L);
|
||||
list.getInt(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFloatPromotion() throws Exception {
|
||||
BdfList list = new BdfList();
|
||||
@@ -88,6 +63,61 @@ public class BdfListTest extends BrambleTestCase {
|
||||
assertArrayEquals(new byte[123], second);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public void testIndexOutOfBoundsReturnsDefaultValue() throws Exception {
|
||||
BdfList list = BdfList.of(1, 2, 3);
|
||||
boolean defaultBoolean = true;
|
||||
assertEquals(defaultBoolean, list.getBoolean(-1, defaultBoolean));
|
||||
assertEquals(defaultBoolean, list.getBoolean(3, defaultBoolean));
|
||||
Long defaultLong = 123L;
|
||||
assertEquals(defaultLong, list.getLong(-1, defaultLong));
|
||||
assertEquals(defaultLong, list.getLong(3, defaultLong));
|
||||
Double defaultDouble = 1.23;
|
||||
assertEquals(defaultDouble, list.getDouble(-1, defaultDouble));
|
||||
assertEquals(defaultDouble, list.getDouble(3, defaultDouble));
|
||||
String defaultString = "123";
|
||||
assertEquals(defaultString, list.getString(-1, defaultString));
|
||||
assertEquals(defaultString, list.getString(3, defaultString));
|
||||
byte[] defaultBytes = new byte[] {1, 2, 3};
|
||||
assertArrayEquals(defaultBytes, list.getRaw(-1, defaultBytes));
|
||||
assertArrayEquals(defaultBytes, list.getRaw(3, defaultBytes));
|
||||
BdfList defaultList = BdfList.of(1, 2, 3);
|
||||
assertEquals(defaultList, list.getList(-1, defaultList));
|
||||
assertEquals(defaultList, list.getList(3, defaultList));
|
||||
BdfDictionary defaultDict = BdfDictionary.of(
|
||||
new BdfEntry("1", 1),
|
||||
new BdfEntry("2", 2),
|
||||
new BdfEntry("3", 3)
|
||||
);
|
||||
assertEquals(defaultDict, list.getDictionary(-1, defaultDict));
|
||||
assertEquals(defaultDict, list.getDictionary(3, defaultDict));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public void testWrongTypeReturnsDefaultValue() throws Exception {
|
||||
BdfList list = BdfList.of(1, 2, 3, true);
|
||||
boolean defaultBoolean = true;
|
||||
assertEquals(defaultBoolean, list.getBoolean(0, defaultBoolean));
|
||||
Long defaultLong = 123L;
|
||||
assertEquals(defaultLong, list.getLong(3, defaultLong));
|
||||
Double defaultDouble = 1.23;
|
||||
assertEquals(defaultDouble, list.getDouble(0, defaultDouble));
|
||||
String defaultString = "123";
|
||||
assertEquals(defaultString, list.getString(0, defaultString));
|
||||
byte[] defaultBytes = new byte[] {1, 2, 3};
|
||||
assertArrayEquals(defaultBytes, list.getRaw(0, defaultBytes));
|
||||
BdfList defaultList = BdfList.of(1, 2, 3);
|
||||
assertEquals(defaultList, list.getList(0, defaultList));
|
||||
BdfDictionary defaultDict = BdfDictionary.of(
|
||||
new BdfEntry("1", 1),
|
||||
new BdfEntry("2", 2),
|
||||
new BdfEntry("3", 3)
|
||||
);
|
||||
assertEquals(defaultDict, list.getDictionary(0, defaultDict));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -100,12 +130,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalBoolean(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getBoolean(-1, true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForLongThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -118,30 +142,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalLong(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultLongThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getLong(-1, 1L);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getInt(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForOptionalIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getOptionalInt(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getInt(-1, 1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -154,12 +154,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalDouble(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDouble(-1, 1D);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForStringThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -172,12 +166,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalString(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultStringThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getString(-1, "");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForRawThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -190,12 +178,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalRaw(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultRawThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getRaw(-1, new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForListThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -208,11 +190,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalList(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultListThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getList(-1, new BdfList());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDictionaryThrowsFormatException()
|
||||
@@ -226,12 +203,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalDictionary(-1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNegativeIndexForDefaultDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDictionary(-1, new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -244,12 +215,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalBoolean(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getBoolean(0, true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForLongThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -262,30 +227,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalLong(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultLongThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getLong(0, 1L);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForOptionalIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getOptionalInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultIntThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getInt(0, 1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -298,12 +239,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalDouble(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDouble(0, 1D);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForStringThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -316,12 +251,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalString(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultStringThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getString(0, "");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForRawThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -334,12 +263,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalRaw(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultRawThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getRaw(0, new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForListThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -352,11 +275,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
new BdfList().getOptionalList(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultListThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getList(0, new BdfList());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDictionaryThrowsFormatException()
|
||||
@@ -369,13 +287,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
throws Exception {
|
||||
new BdfList().getOptionalDictionary(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testTooLargeIndexForDefaultDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
new BdfList().getDictionary(0, new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
@@ -388,12 +299,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalBoolean(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getBoolean(0, true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForLongThrowsFormatException() throws Exception {
|
||||
BdfList.of(1.23).getLong(0);
|
||||
@@ -405,29 +310,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(1.23).getOptionalLong(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultLongThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(1.23).getLong(0, 1L);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForIntThrowsFormatException() throws Exception {
|
||||
BdfList.of(1.23).getInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForOptionalIntThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(1.23).getOptionalInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultIntThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(1.23).getInt(0, 1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDoubleThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getDouble(0);
|
||||
@@ -439,12 +321,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalDouble(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultDoubleThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getDouble(0, 1D);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForStringThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getString(0);
|
||||
@@ -456,12 +332,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalString(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultStringThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getString(0, "");
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForRawThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getRaw(0);
|
||||
@@ -473,12 +343,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalRaw(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultRawThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getRaw(0, new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForListThrowsFormatException() throws Exception {
|
||||
BdfList.of(123).getList(0);
|
||||
@@ -490,11 +354,6 @@ public class BdfListTest extends BrambleTestCase {
|
||||
BdfList.of(123).getOptionalList(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultListThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getList(0, new BdfList());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDictionaryThrowsFormatException()
|
||||
@@ -507,81 +366,4 @@ public class BdfListTest extends BrambleTestCase {
|
||||
throws Exception {
|
||||
BdfList.of(123).getOptionalDictionary(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testWrongTypeForDefaultDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(123).getDictionary(0, new BdfDictionary());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForBooleanThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(NULL_VALUE).getBoolean(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForLongThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getLong(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForIntThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getInt(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForDoubleThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getDouble(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForStringThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getString(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForRawThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getRaw(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForListThrowsFormatException() throws Exception {
|
||||
BdfList.of(NULL_VALUE).getList(0);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testNullValueForDictionaryThrowsFormatException()
|
||||
throws Exception {
|
||||
BdfList.of(NULL_VALUE).getDictionary(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalMethodsReturnNullForNullValue() throws Exception {
|
||||
BdfList list = BdfList.of(NULL_VALUE);
|
||||
assertNull(list.getOptionalBoolean(0));
|
||||
assertNull(list.getOptionalLong(0));
|
||||
assertNull(list.getOptionalInt(0));
|
||||
assertNull(list.getOptionalDouble(0));
|
||||
assertNull(list.getOptionalString(0));
|
||||
assertNull(list.getOptionalRaw(0));
|
||||
assertNull(list.getOptionalList(0));
|
||||
assertNull(list.getOptionalDictionary(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultMethodsReturnDefaultForNullValue() throws Exception {
|
||||
BdfList list = BdfList.of(NULL_VALUE);
|
||||
assertEquals(TRUE, list.getBoolean(0, TRUE));
|
||||
assertEquals(Long.valueOf(123L), list.getLong(0, 123L));
|
||||
assertEquals(Integer.valueOf(123), list.getInt(0, 123));
|
||||
assertEquals(Double.valueOf(123D), list.getDouble(0, 123D));
|
||||
assertEquals("123", list.getString(0, "123"));
|
||||
byte[] defaultRaw = {1, 2, 3};
|
||||
assertArrayEquals(defaultRaw, list.getRaw(0, defaultRaw));
|
||||
BdfList defaultList = BdfList.of(1, 2, 3);
|
||||
assertEquals(defaultList, list.getList(0, defaultList));
|
||||
BdfDictionary defaultDict = BdfDictionary.of(new BdfEntry("123", 123));
|
||||
assertEquals(defaultDict, list.getDictionary(0, defaultDict));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||
'com.fasterxml.jackson.core:jackson-annotations:2.13.4:jackson-annotations-2.13.4.jar:ac5b27a634942391ca113850ee7db01df1499a240174021263501c05fc653b44',
|
||||
'com.fasterxml.jackson.core:jackson-annotations:2.13.0:jackson-annotations-2.13.0.jar:81f9724d8843e8b08f8f6c0609e7a2b030d00c34861c4ac7e2099a7235047d6f',
|
||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
||||
'com.google.dagger:dagger:2.33:dagger-2.33.jar:d8798c5b8cf6b125234e33af5c6293bb9f2208ce29b57924c35b8c0be7b6bdcb',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||
|
||||
@@ -9,31 +9,30 @@ apply from: 'witness.gradle'
|
||||
apply from: '../dagger.gradle'
|
||||
|
||||
dependencies {
|
||||
api project(':bramble-api')
|
||||
|
||||
api "org.briarproject:onionwrapper-core:$onionwrapper_version"
|
||||
|
||||
implementation "org.bouncycastle:bcprov-jdk15to18:$bouncy_castle_version"
|
||||
implementation project(path: ':bramble-api', configuration: 'default')
|
||||
implementation 'org.bouncycastle:bcprov-jdk15to18:1.70'
|
||||
//noinspection GradleDependency
|
||||
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||
implementation 'org.bitlet:weupnp:0.1.4'
|
||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||
implementation 'org.whispersystems:curve25519-java:0.5.0'
|
||||
implementation 'org.briarproject:jtorctl:0.5'
|
||||
implementation 'org.briarproject:socks-socket:0.1'
|
||||
|
||||
//noinspection GradleDependency
|
||||
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
|
||||
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
|
||||
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
||||
testImplementation 'net.jodah:concurrentunit:0.4.2'
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testImplementation "org.jmock:jmock:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:$mockwebserver_version"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3"
|
||||
|
||||
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||
|
||||
@@ -53,7 +52,7 @@ configurations {
|
||||
testOutput.extendsFrom(testCompile)
|
||||
}
|
||||
task jarTest(type: Jar, dependsOn: testClasses) {
|
||||
from sourceSets.test.output, sourceSets.main.output
|
||||
from sourceSets.test.output
|
||||
classifier = 'test'
|
||||
}
|
||||
artifacts {
|
||||
|
||||
@@ -17,7 +17,6 @@ import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||
import org.briarproject.bramble.mailbox.MailboxModule;
|
||||
import org.briarproject.bramble.plugin.PluginModule;
|
||||
import org.briarproject.bramble.properties.PropertiesModule;
|
||||
import org.briarproject.bramble.qrcode.QrCodeModule;
|
||||
import org.briarproject.bramble.record.RecordModule;
|
||||
import org.briarproject.bramble.reliability.ReliabilityModule;
|
||||
import org.briarproject.bramble.rendezvous.RendezvousModule;
|
||||
@@ -48,7 +47,6 @@ import dagger.Module;
|
||||
MailboxModule.class,
|
||||
PluginModule.class,
|
||||
PropertiesModule.class,
|
||||
QrCodeModule.class,
|
||||
RecordModule.class,
|
||||
ReliabilityModule.class,
|
||||
RendezvousModule.class,
|
||||
|
||||
@@ -4,7 +4,6 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
@@ -20,10 +19,9 @@ public class TimeLoggingExecutor extends ThreadPoolExecutor {
|
||||
public TimeLoggingExecutor(String tag, int corePoolSize, int maxPoolSize,
|
||||
long keepAliveTime, TimeUnit unit,
|
||||
BlockingQueue<Runnable> workQueue,
|
||||
ThreadFactory threadFactory,
|
||||
RejectedExecutionHandler handler) {
|
||||
super(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue,
|
||||
threadFactory, handler);
|
||||
handler);
|
||||
log = Logger.getLogger(tag);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -28,7 +29,6 @@ import javax.inject.Inject;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
|
||||
@@ -99,7 +99,7 @@ class AccountManagerImpl implements AccountManager {
|
||||
}
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(f), UTF_8));
|
||||
new FileInputStream(f), Charset.forName("UTF-8")));
|
||||
String key = reader.readLine();
|
||||
reader.close();
|
||||
return key;
|
||||
@@ -151,7 +151,7 @@ class AccountManagerImpl implements AccountManager {
|
||||
@GuardedBy("stateChangeLock")
|
||||
private void writeDbKeyToFile(String key, File f) throws IOException {
|
||||
FileOutputStream out = new FileOutputStream(f);
|
||||
out.write(key.getBytes(UTF_8));
|
||||
out.write(key.getBytes(Charset.forName("UTF-8")));
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
@@ -155,13 +155,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
@Override
|
||||
public BdfList getMessageAsList(Transaction txn, MessageId m)
|
||||
throws DbException, FormatException {
|
||||
return getMessageAsList(txn, m, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList getMessageAsList(Transaction txn, MessageId m,
|
||||
boolean canonical) throws DbException, FormatException {
|
||||
return toList(db.getMessage(txn, m), canonical);
|
||||
return toList(db.getMessage(txn, m).getBody());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -319,13 +313,8 @@ class ClientHelperImpl implements ClientHelper {
|
||||
|
||||
@Override
|
||||
public BdfList toList(byte[] b, int off, int len) throws FormatException {
|
||||
return toList(b, off, len, true);
|
||||
}
|
||||
|
||||
private BdfList toList(byte[] b, int off, int len, boolean canonical)
|
||||
throws FormatException {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b, off, len);
|
||||
BdfReader reader = bdfReaderFactory.createReader(in, canonical);
|
||||
BdfReader reader = bdfReaderFactory.createReader(in);
|
||||
try {
|
||||
BdfList list = reader.readList();
|
||||
if (!reader.eof()) throw new FormatException();
|
||||
@@ -339,7 +328,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
|
||||
@Override
|
||||
public BdfList toList(byte[] b) throws FormatException {
|
||||
return toList(b, 0, b.length, true);
|
||||
return toList(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -347,12 +336,6 @@ class ClientHelperImpl implements ClientHelper {
|
||||
return toList(m.getBody());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList toList(Message m, boolean canonical) throws FormatException {
|
||||
byte[] b = m.getBody();
|
||||
return toList(b, 0, b.length, canonical);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList toList(Author a) {
|
||||
return BdfList.of(a.getFormatVersion(), a.getName(), a.getPublicKey());
|
||||
@@ -378,7 +361,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
public Author parseAndValidateAuthor(BdfList author)
|
||||
throws FormatException {
|
||||
checkSize(author, 3);
|
||||
int formatVersion = author.getInt(0);
|
||||
int formatVersion = author.getLong(0).intValue();
|
||||
if (formatVersion != FORMAT_VERSION) throw new FormatException();
|
||||
String name = author.getString(1);
|
||||
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
|
||||
@@ -489,7 +472,8 @@ class ClientHelperImpl implements ClientHelper {
|
||||
if (element.size() != 2) {
|
||||
throw new FormatException();
|
||||
}
|
||||
list.add(new MailboxVersion(element.getInt(0), element.getInt(1)));
|
||||
list.add(new MailboxVersion(element.getLong(0).intValue(),
|
||||
element.getLong(1).intValue()));
|
||||
}
|
||||
// Sort the list of versions for easier comparison
|
||||
sort(list);
|
||||
@@ -502,7 +486,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
try {
|
||||
BdfDictionary meta =
|
||||
getGroupMetadataAsDictionary(txn, contactGroupId);
|
||||
return new ContactId(meta.getInt(GROUP_KEY_CONTACT_ID));
|
||||
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e); // Invalid group metadata
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.whispersystems.curve25519.Curve25519;
|
||||
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
@@ -50,7 +51,6 @@ import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHE
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
|
||||
|
||||
@NotNullByDefault
|
||||
class CryptoComponentImpl implements CryptoComponent {
|
||||
@@ -460,7 +460,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
@Override
|
||||
public String encodeOnion(byte[] publicKey) {
|
||||
Digest digest = new SHA3Digest(256);
|
||||
byte[] label = ".onion checksum".getBytes(US_ASCII);
|
||||
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
|
||||
digest.update(label, 0, label.length);
|
||||
digest.update(publicKey, 0, publicKey.length);
|
||||
digest.update(ONION_HS_PROTOCOL_VERSION);
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -38,31 +37,31 @@ public class CryptoExecutorModule {
|
||||
private static final int MAX_EXECUTOR_THREADS =
|
||||
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
|
||||
|
||||
public CryptoExecutorModule() {
|
||||
}
|
||||
private final ExecutorService cryptoExecutor;
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@CryptoExecutor
|
||||
ExecutorService provideCryptoExecutorService(
|
||||
LifecycleManager lifecycleManager, ThreadFactory threadFactory) {
|
||||
public CryptoExecutorModule() {
|
||||
// Use an unbounded queue
|
||||
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
||||
// Discard tasks that are submitted during shutdown
|
||||
RejectedExecutionHandler policy =
|
||||
new ThreadPoolExecutor.DiscardPolicy();
|
||||
// Create a limited # of threads and keep them in the pool for 60 secs
|
||||
ExecutorService cryptoExecutor = new TimeLoggingExecutor(
|
||||
"CryptoExecutor", 0, MAX_EXECUTOR_THREADS, 60, SECONDS, queue,
|
||||
threadFactory, policy);
|
||||
cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
|
||||
MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@CryptoExecutor
|
||||
ExecutorService provideCryptoExecutorService(
|
||||
LifecycleManager lifecycleManager) {
|
||||
lifecycleManager.registerForShutdown(cryptoExecutor);
|
||||
return cryptoExecutor;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@CryptoExecutor
|
||||
Executor provideCryptoExecutor(
|
||||
@CryptoExecutor ExecutorService cryptoExecutor) {
|
||||
Executor provideCryptoExecutor() {
|
||||
return cryptoExecutor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,13 +39,12 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Scanner;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class MessageEncrypter {
|
||||
@@ -229,7 +228,7 @@ public class MessageEncrypter {
|
||||
PublicKey publicKey =
|
||||
encrypter.getKeyParser().parsePublicKey(keyBytes);
|
||||
String message = readFully(System.in);
|
||||
byte[] plaintext = message.getBytes(UTF_8);
|
||||
byte[] plaintext = message.getBytes(Charset.forName("UTF-8"));
|
||||
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
|
||||
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
|
||||
}
|
||||
@@ -243,7 +242,7 @@ public class MessageEncrypter {
|
||||
encrypter.getKeyParser().parsePrivateKey(keyBytes);
|
||||
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
|
||||
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
|
||||
System.out.println(new String(plaintext, UTF_8));
|
||||
System.out.println(new String(plaintext, Charset.forName("UTF-8")));
|
||||
}
|
||||
|
||||
private static String readFully(InputStream in) throws IOException {
|
||||
|
||||
@@ -18,18 +18,12 @@ class BdfReaderFactoryImpl implements BdfReaderFactory {
|
||||
@Override
|
||||
public BdfReader createReader(InputStream in) {
|
||||
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||
DEFAULT_MAX_BUFFER_SIZE, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfReader createReader(InputStream in, boolean canonical) {
|
||||
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
|
||||
DEFAULT_MAX_BUFFER_SIZE, canonical);
|
||||
DEFAULT_MAX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfReader createReader(InputStream in, int nestedLimit,
|
||||
int maxBufferSize, boolean canonical) {
|
||||
return new BdfReaderImpl(in, nestedLimit, maxBufferSize, canonical);
|
||||
int maxBufferSize) {
|
||||
return new BdfReaderImpl(in, nestedLimit, maxBufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,24 +33,21 @@ import static org.briarproject.bramble.util.StringUtils.fromUtf8;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
final class BdfReaderImpl implements BdfReader {
|
||||
class BdfReaderImpl implements BdfReader {
|
||||
|
||||
private static final byte[] EMPTY_BUFFER = new byte[0];
|
||||
|
||||
private final InputStream in;
|
||||
private final int nestedLimit, maxBufferSize;
|
||||
private final boolean canonical;
|
||||
|
||||
private boolean hasLookahead = false, eof = false;
|
||||
private byte next;
|
||||
private byte[] buf = new byte[8];
|
||||
|
||||
BdfReaderImpl(InputStream in, int nestedLimit, int maxBufferSize,
|
||||
boolean canonical) {
|
||||
BdfReaderImpl(InputStream in, int nestedLimit, int maxBufferSize) {
|
||||
this.in = in;
|
||||
this.nestedLimit = nestedLimit;
|
||||
this.maxBufferSize = maxBufferSize;
|
||||
this.canonical = canonical;
|
||||
}
|
||||
|
||||
private void readLookahead() throws IOException {
|
||||
@@ -191,22 +188,13 @@ final class BdfReaderImpl implements BdfReader {
|
||||
|
||||
private short readInt16() throws IOException {
|
||||
readIntoBuffer(2);
|
||||
short value = (short) (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF));
|
||||
if (canonical && value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
||||
// Value could have been encoded as an INT_8
|
||||
throw new FormatException();
|
||||
}
|
||||
return value;
|
||||
return (short) (((buf[0] & 0xFF) << 8) + (buf[1] & 0xFF));
|
||||
}
|
||||
|
||||
private int readInt32() throws IOException {
|
||||
readIntoBuffer(4);
|
||||
int value = 0;
|
||||
for (int i = 0; i < 4; i++) value |= (buf[i] & 0xFF) << (24 - i * 8);
|
||||
if (canonical && value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
||||
// Value could have been encoded as an INT_16
|
||||
throw new FormatException();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -214,11 +202,6 @@ final class BdfReaderImpl implements BdfReader {
|
||||
readIntoBuffer(8);
|
||||
long value = 0;
|
||||
for (int i = 0; i < 8; i++) value |= (buf[i] & 0xFFL) << (56 - i * 8);
|
||||
if (canonical && value >= Integer.MIN_VALUE &&
|
||||
value <= Integer.MAX_VALUE) {
|
||||
// Value could have been encoded as an INT_32
|
||||
throw new FormatException();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -232,31 +215,6 @@ final class BdfReaderImpl implements BdfReader {
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasInt() throws IOException {
|
||||
if (!hasLookahead) readLookahead();
|
||||
if (eof) return false;
|
||||
return next == INT_8 || next == INT_16 || next == INT_32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
if (!hasInt()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
if (next == INT_8) return readInt8();
|
||||
if (next == INT_16) return readInt16();
|
||||
return readInt32();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skipInt() throws IOException {
|
||||
if (!hasInt()) throw new FormatException();
|
||||
if (next == INT_8) skip(1);
|
||||
else if (next == INT_16) skip(2);
|
||||
else skip(4);
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDouble() throws IOException {
|
||||
if (!hasLookahead) readLookahead();
|
||||
@@ -365,19 +323,35 @@ final class BdfReaderImpl implements BdfReader {
|
||||
private BdfList readList(int level) throws IOException {
|
||||
if (!hasList()) throw new FormatException();
|
||||
if (level > nestedLimit) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
BdfList list = new BdfList();
|
||||
while (!hasEnd()) list.add(readObject(level + 1));
|
||||
readEnd();
|
||||
readListStart();
|
||||
while (!hasListEnd()) list.add(readObject(level + 1));
|
||||
readListEnd();
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readListStart() throws IOException {
|
||||
if (!hasList()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasListEnd() throws IOException {
|
||||
return hasEnd();
|
||||
}
|
||||
|
||||
private boolean hasEnd() throws IOException {
|
||||
if (!hasLookahead) readLookahead();
|
||||
if (eof) return false;
|
||||
return next == END;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readListEnd() throws IOException {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
private void readEnd() throws IOException {
|
||||
if (!hasEnd()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
@@ -387,7 +361,7 @@ final class BdfReaderImpl implements BdfReader {
|
||||
public void skipList() throws IOException {
|
||||
if (!hasList()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while (!hasEnd()) skipObject();
|
||||
while (!hasListEnd()) skipObject();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@@ -406,27 +380,35 @@ final class BdfReaderImpl implements BdfReader {
|
||||
private BdfDictionary readDictionary(int level) throws IOException {
|
||||
if (!hasDictionary()) throw new FormatException();
|
||||
if (level > nestedLimit) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
BdfDictionary dictionary = new BdfDictionary();
|
||||
String prevKey = null;
|
||||
while (!hasEnd()) {
|
||||
String key = readString();
|
||||
if (canonical && prevKey != null && key.compareTo(prevKey) <= 0) {
|
||||
// Keys not unique and sorted
|
||||
throw new FormatException();
|
||||
}
|
||||
dictionary.put(key, readObject(level + 1));
|
||||
prevKey = key;
|
||||
}
|
||||
readEnd();
|
||||
readDictionaryStart();
|
||||
while (!hasDictionaryEnd())
|
||||
dictionary.put(readString(), readObject(level + 1));
|
||||
readDictionaryEnd();
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readDictionaryStart() throws IOException {
|
||||
if (!hasDictionary()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDictionaryEnd() throws IOException {
|
||||
return hasEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readDictionaryEnd() throws IOException {
|
||||
readEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skipDictionary() throws IOException {
|
||||
if (!hasDictionary()) throw new FormatException();
|
||||
hasLookahead = false;
|
||||
while (!hasEnd()) {
|
||||
while (!hasDictionaryEnd()) {
|
||||
skipString();
|
||||
skipObject();
|
||||
}
|
||||
|
||||
@@ -2,13 +2,11 @@ package org.briarproject.bramble.data;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.data.BdfWriter;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -17,7 +15,6 @@ import java.util.Map.Entry;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
import static java.util.Collections.sort;
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.bramble.data.Types.DICTIONARY;
|
||||
import static org.briarproject.bramble.data.Types.END;
|
||||
@@ -36,11 +33,10 @@ import static org.briarproject.bramble.data.Types.STRING_16;
|
||||
import static org.briarproject.bramble.data.Types.STRING_32;
|
||||
import static org.briarproject.bramble.data.Types.STRING_8;
|
||||
import static org.briarproject.bramble.data.Types.TRUE;
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
final class BdfWriterImpl implements BdfWriter {
|
||||
class BdfWriterImpl implements BdfWriter {
|
||||
|
||||
private final OutputStream out;
|
||||
|
||||
@@ -117,7 +113,7 @@ final class BdfWriterImpl implements BdfWriter {
|
||||
|
||||
@Override
|
||||
public void writeString(String s) throws IOException {
|
||||
byte[] b = s.getBytes(UTF_8);
|
||||
byte[] b = s.getBytes("UTF-8");
|
||||
if (b.length <= Byte.MAX_VALUE) {
|
||||
out.write(STRING_8);
|
||||
out.write((byte) b.length);
|
||||
@@ -165,33 +161,39 @@ final class BdfWriterImpl implements BdfWriter {
|
||||
else if (o instanceof String) writeString((String) o);
|
||||
else if (o instanceof byte[]) writeRaw((byte[]) o);
|
||||
else if (o instanceof Bytes) writeRaw(((Bytes) o).getBytes());
|
||||
else if (o instanceof List) writeList((List<?>) o);
|
||||
else if (o instanceof Map) writeDictionary((Map<?, ?>) o);
|
||||
else if (o instanceof List) writeList((List) o);
|
||||
else if (o instanceof Map) writeDictionary((Map) o);
|
||||
else throw new FormatException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeListStart() throws IOException {
|
||||
out.write(LIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeListEnd() throws IOException {
|
||||
out.write(END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDictionary(Map<?, ?> m) throws IOException {
|
||||
out.write(DICTIONARY);
|
||||
if (m instanceof BdfDictionary) {
|
||||
// Entries are already sorted and keys are known to be strings
|
||||
for (Entry<String, Object> e : ((BdfDictionary) m).entrySet()) {
|
||||
writeString(e.getKey());
|
||||
writeObject(e.getValue());
|
||||
}
|
||||
} else {
|
||||
// Check that keys are strings, write entries in canonical order
|
||||
List<String> keys = new ArrayList<>(m.size());
|
||||
for (Object k : m.keySet()) {
|
||||
if (!(k instanceof String)) throw new FormatException();
|
||||
keys.add((String) k);
|
||||
}
|
||||
sort(keys);
|
||||
for (String key : keys) {
|
||||
writeString(key);
|
||||
writeObject(m.get(key));
|
||||
}
|
||||
for (Entry<?, ?> e : m.entrySet()) {
|
||||
if (!(e.getKey() instanceof String)) throw new FormatException();
|
||||
writeString((String) e.getKey());
|
||||
writeObject(e.getValue());
|
||||
}
|
||||
out.write(END);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDictionaryStart() throws IOException {
|
||||
out.write(DICTIONARY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDictionaryEnd() throws IOException {
|
||||
out.write(END);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -29,20 +28,24 @@ public class DatabaseExecutorModule {
|
||||
ExecutorService executorService;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@DatabaseExecutor
|
||||
ExecutorService provideDatabaseExecutorService(
|
||||
LifecycleManager lifecycleManager, ThreadFactory threadFactory) {
|
||||
private final ExecutorService databaseExecutor;
|
||||
|
||||
public DatabaseExecutorModule() {
|
||||
// Use an unbounded queue
|
||||
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
||||
// Discard tasks that are submitted during shutdown
|
||||
RejectedExecutionHandler policy =
|
||||
new ThreadPoolExecutor.DiscardPolicy();
|
||||
// Use a single thread and keep it in the pool for 60 secs
|
||||
ExecutorService databaseExecutor = new TimeLoggingExecutor(
|
||||
"DatabaseExecutor", 0, 1, 60, SECONDS, queue, threadFactory,
|
||||
policy);
|
||||
databaseExecutor = new TimeLoggingExecutor("DatabaseExecutor", 0, 1,
|
||||
60, SECONDS, queue, policy);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@DatabaseExecutor
|
||||
ExecutorService provideDatabaseExecutorService(
|
||||
LifecycleManager lifecycleManager) {
|
||||
lifecycleManager.registerForShutdown(databaseExecutor);
|
||||
return databaseExecutor;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.event;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -23,11 +22,10 @@ public class DefaultEventExecutorModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
@EventExecutor
|
||||
Executor provideEventExecutor(ThreadFactory threadFactory) {
|
||||
Executor provideEventExecutor() {
|
||||
return newSingleThreadExecutor(r -> {
|
||||
Thread t = threadFactory.newThread(r);
|
||||
Thread t = new Thread(r);
|
||||
t.setDaemon(true);
|
||||
t.setName(t.getName() + "-Event");
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||
@@ -17,13 +19,13 @@ public class KeyAgreementModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
PayloadEncoder providePayloadEncoder(PayloadEncoderImpl payloadEncoder) {
|
||||
return payloadEncoder;
|
||||
PayloadEncoder providePayloadEncoder(BdfWriterFactory bdfWriterFactory) {
|
||||
return new PayloadEncoderImpl(bdfWriterFactory);
|
||||
}
|
||||
|
||||
@Provides
|
||||
PayloadParser providePayloadParser(PayloadParserImpl payloadParser) {
|
||||
return payloadParser;
|
||||
PayloadParser providePayloadParser(BdfReaderFactory bdfReaderFactory) {
|
||||
return new PayloadParserImpl(bdfReaderFactory);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.data.BdfWriter;
|
||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
@@ -14,8 +13,7 @@ import java.io.IOException;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_ID;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -31,16 +29,14 @@ class PayloadEncoderImpl implements PayloadEncoder {
|
||||
@Override
|
||||
public byte[] encode(Payload p) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int formatIdAndVersion = (QR_FORMAT_ID << 5) | QR_FORMAT_VERSION;
|
||||
out.write(formatIdAndVersion);
|
||||
BdfList payload = new BdfList();
|
||||
payload.add(p.getCommitment());
|
||||
for (TransportDescriptor d : p.getTransportDescriptors()) {
|
||||
payload.add(d.getDescriptor());
|
||||
}
|
||||
out.write(PROTOCOL_VERSION);
|
||||
BdfWriter w = bdfWriterFactory.createWriter(out);
|
||||
try {
|
||||
w.writeList(payload);
|
||||
w.writeListStart(); // Payload start
|
||||
w.writeRaw(p.getCommitment());
|
||||
for (TransportDescriptor d : p.getTransportDescriptors())
|
||||
w.writeList(d.getDescriptor());
|
||||
w.writeListEnd(); // Payload end
|
||||
} catch (IOException e) {
|
||||
// Shouldn't happen with ByteArrayOutputStream
|
||||
throw new AssertionError(e);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.data.BdfReader;
|
||||
@@ -12,9 +11,6 @@ import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
|
||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -25,42 +21,34 @@ import java.util.List;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.BETA_PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class PayloadParserImpl implements PayloadParser {
|
||||
|
||||
private final BdfReaderFactory bdfReaderFactory;
|
||||
private final QrCodeClassifier qrCodeClassifier;
|
||||
|
||||
@Inject
|
||||
PayloadParserImpl(BdfReaderFactory bdfReaderFactory,
|
||||
QrCodeClassifier qrCodeClassifier) {
|
||||
PayloadParserImpl(BdfReaderFactory bdfReaderFactory) {
|
||||
this.bdfReaderFactory = bdfReaderFactory;
|
||||
this.qrCodeClassifier = qrCodeClassifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload parse(String payloadString) throws IOException {
|
||||
Pair<QrCodeType, Integer> typeAndVersion =
|
||||
qrCodeClassifier.classifyQrCode(payloadString);
|
||||
QrCodeType qrCodeType = typeAndVersion.getFirst();
|
||||
if (qrCodeType != BQP) throw new WrongQrCodeTypeException(qrCodeType);
|
||||
int formatVersion = typeAndVersion.getSecond();
|
||||
if (formatVersion != QR_FORMAT_VERSION) {
|
||||
boolean tooOld = formatVersion < QR_FORMAT_VERSION;
|
||||
public Payload parse(byte[] raw) throws IOException {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(raw);
|
||||
// First byte: the protocol version
|
||||
int protocolVersion = in.read();
|
||||
if (protocolVersion == -1) throw new FormatException();
|
||||
if (protocolVersion != PROTOCOL_VERSION) {
|
||||
boolean tooOld = protocolVersion < PROTOCOL_VERSION ||
|
||||
protocolVersion == BETA_PROTOCOL_VERSION;
|
||||
throw new UnsupportedVersionException(tooOld);
|
||||
}
|
||||
byte[] raw = payloadString.getBytes(ISO_8859_1);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(raw);
|
||||
// First byte: the format identifier and version (already parsed)
|
||||
if (in.read() == -1) throw new AssertionError();
|
||||
// The rest of the payload is a BDF list with one or more elements
|
||||
BdfReader r = bdfReaderFactory.createReader(in);
|
||||
BdfList payload = r.readList();
|
||||
@@ -73,7 +61,7 @@ class PayloadParserImpl implements PayloadParser {
|
||||
List<TransportDescriptor> recognised = new ArrayList<>();
|
||||
for (int i = 1; i < payload.size(); i++) {
|
||||
BdfList descriptor = payload.getList(i);
|
||||
int transportId = descriptor.getInt(0);
|
||||
long transportId = descriptor.getLong(0);
|
||||
if (transportId == TRANSPORT_ID_BLUETOOTH) {
|
||||
TransportId id = BluetoothConstants.ID;
|
||||
recognised.add(new TransportDescriptor(id, descriptor));
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -29,6 +28,19 @@ public class LifecycleModule {
|
||||
Executor executor;
|
||||
}
|
||||
|
||||
private final ExecutorService ioExecutor;
|
||||
|
||||
public LifecycleModule() {
|
||||
// The thread pool is unbounded, so use direct handoff
|
||||
BlockingQueue<Runnable> queue = new SynchronousQueue<>();
|
||||
// Discard tasks that are submitted during shutdown
|
||||
RejectedExecutionHandler policy =
|
||||
new ThreadPoolExecutor.DiscardPolicy();
|
||||
// Create threads as required and keep them in the pool for 60 seconds
|
||||
ioExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
|
||||
60, SECONDS, queue, policy);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ShutdownManager provideShutdownManager() {
|
||||
@@ -45,16 +57,7 @@ public class LifecycleModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
@IoExecutor
|
||||
Executor provideIoExecutor(LifecycleManager lifecycleManager,
|
||||
ThreadFactory threadFactory) {
|
||||
// The thread pool is unbounded, so use direct handoff
|
||||
BlockingQueue<Runnable> queue = new SynchronousQueue<>();
|
||||
// Discard tasks that are submitted during shutdown
|
||||
RejectedExecutionHandler policy =
|
||||
new ThreadPoolExecutor.DiscardPolicy();
|
||||
// Create threads as required and keep them in the pool for 60 seconds
|
||||
ExecutorService ioExecutor = new ThreadPoolExecutor(0,
|
||||
Integer.MAX_VALUE, 60, SECONDS, queue, threadFactory, policy);
|
||||
Executor provideIoExecutor(LifecycleManager lifecycleManager) {
|
||||
lifecycleManager.registerForShutdown(ioExecutor);
|
||||
return ioExecutor;
|
||||
}
|
||||
|
||||
@@ -20,15 +20,12 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
class MailboxApiCallerImpl implements MailboxApiCaller {
|
||||
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final MailboxConfig mailboxConfig;
|
||||
private final Executor ioExecutor;
|
||||
|
||||
@Inject
|
||||
MailboxApiCallerImpl(TaskScheduler taskScheduler,
|
||||
MailboxConfig mailboxConfig,
|
||||
@IoExecutor Executor ioExecutor) {
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.mailboxConfig = mailboxConfig;
|
||||
this.ioExecutor = ioExecutor;
|
||||
}
|
||||
|
||||
@@ -52,8 +49,7 @@ class MailboxApiCallerImpl implements MailboxApiCaller {
|
||||
private boolean cancelled = false;
|
||||
|
||||
@GuardedBy("lock")
|
||||
private long retryIntervalMs =
|
||||
mailboxConfig.getApiCallerMinRetryInterval();
|
||||
private long retryIntervalMs = MIN_RETRY_INTERVAL_MS;
|
||||
|
||||
private Task(ApiCall apiCall) {
|
||||
this.apiCall = apiCall;
|
||||
@@ -78,9 +74,8 @@ class MailboxApiCallerImpl implements MailboxApiCaller {
|
||||
scheduledTask = taskScheduler.schedule(this::callApi,
|
||||
ioExecutor, retryIntervalMs, MILLISECONDS);
|
||||
// Increase the retry interval each time we retry
|
||||
retryIntervalMs = min(
|
||||
mailboxConfig.getApiCallerMaxRetryInterval(),
|
||||
retryIntervalMs * 2);
|
||||
retryIntervalMs =
|
||||
min(MAX_RETRY_INTERVAL_MS, retryIntervalMs * 2);
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.Plugin;
|
||||
|
||||
interface MailboxConfig {
|
||||
|
||||
/**
|
||||
* The minimum interval between API call retries in milliseconds.
|
||||
*/
|
||||
long getApiCallerMinRetryInterval();
|
||||
|
||||
/**
|
||||
* The maximum interval between API call retries in milliseconds.
|
||||
*/
|
||||
long getApiCallerMaxRetryInterval();
|
||||
|
||||
/**
|
||||
* How long (in milliseconds) the Tor plugin needs to be continuously
|
||||
* {@link Plugin.State#ACTIVE active} before we assume our contacts can
|
||||
* reach our hidden service.
|
||||
*/
|
||||
long getTorReachabilityPeriod();
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class MailboxConfigImpl implements MailboxConfig {
|
||||
|
||||
@Inject
|
||||
MailboxConfigImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getApiCallerMinRetryInterval() {
|
||||
return MailboxApiCaller.MIN_RETRY_INTERVAL_MS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getApiCallerMaxRetryInterval() {
|
||||
return MailboxApiCaller.MAX_RETRY_INTERVAL_MS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTorReachabilityPeriod() {
|
||||
return TorReachabilityMonitor.REACHABILITY_PERIOD_MS;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
@@ -12,15 +11,12 @@ import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.util.Base32;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
@@ -30,7 +26,6 @@ import javax.inject.Inject;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -103,22 +98,6 @@ class MailboxManagerImpl implements MailboxManager {
|
||||
return created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertBase32Payload(String base32Payload)
|
||||
throws FormatException {
|
||||
Pattern regex = Pattern.compile("(briar-mailbox://)?([a-z2-7]{104})");
|
||||
Matcher matcher = regex.matcher(base32Payload);
|
||||
if (!matcher.find()) throw new FormatException();
|
||||
String base32 = matcher.group(2);
|
||||
byte[] payloadBytes;
|
||||
try {
|
||||
payloadBytes = Base32.decode(base32, false);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new FormatException();
|
||||
}
|
||||
return new String(payloadBytes, ISO_8859_1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkConnection() {
|
||||
List<MailboxVersion> versions = null;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.FeatureFlags;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||
@@ -75,11 +76,14 @@ public class MailboxModule {
|
||||
ValidationManager validationManager,
|
||||
ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder,
|
||||
Clock clock) {
|
||||
Clock clock,
|
||||
FeatureFlags featureFlags) {
|
||||
MailboxUpdateValidator validator = new MailboxUpdateValidator(
|
||||
clientHelper, metadataEncoder, clock);
|
||||
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
|
||||
validator);
|
||||
if (featureFlags.shouldEnableMailbox()) {
|
||||
validationManager.registerMessageValidator(CLIENT_ID,
|
||||
MAJOR_VERSION, validator);
|
||||
}
|
||||
return validator;
|
||||
}
|
||||
|
||||
@@ -91,26 +95,31 @@ public class MailboxModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
MailboxUpdateManager provideMailboxUpdateManager(
|
||||
FeatureFlags featureFlags,
|
||||
LifecycleManager lifecycleManager,
|
||||
ValidationManager validationManager, ContactManager contactManager,
|
||||
ClientVersioningManager clientVersioningManager,
|
||||
MailboxSettingsManager mailboxSettingsManager,
|
||||
MailboxUpdateManagerImpl mailboxUpdateManager) {
|
||||
lifecycleManager.registerOpenDatabaseHook(mailboxUpdateManager);
|
||||
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
|
||||
mailboxUpdateManager);
|
||||
contactManager.registerContactHook(mailboxUpdateManager);
|
||||
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
|
||||
MINOR_VERSION, mailboxUpdateManager);
|
||||
mailboxSettingsManager.registerMailboxHook(mailboxUpdateManager);
|
||||
if (featureFlags.shouldEnableMailbox()) {
|
||||
lifecycleManager.registerOpenDatabaseHook(mailboxUpdateManager);
|
||||
validationManager.registerIncomingMessageHook(CLIENT_ID,
|
||||
MAJOR_VERSION, mailboxUpdateManager);
|
||||
contactManager.registerContactHook(mailboxUpdateManager);
|
||||
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
|
||||
MINOR_VERSION, mailboxUpdateManager);
|
||||
mailboxSettingsManager.registerMailboxHook(mailboxUpdateManager);
|
||||
}
|
||||
return mailboxUpdateManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
MailboxFileManager provideMailboxFileManager(EventBus eventBus,
|
||||
MailboxFileManagerImpl mailboxFileManager) {
|
||||
eventBus.addListener(mailboxFileManager);
|
||||
MailboxFileManager provideMailboxFileManager(FeatureFlags featureFlags,
|
||||
EventBus eventBus, MailboxFileManagerImpl mailboxFileManager) {
|
||||
if (featureFlags.shouldEnableMailbox()) {
|
||||
eventBus.addListener(mailboxFileManager);
|
||||
}
|
||||
return mailboxFileManager;
|
||||
}
|
||||
|
||||
@@ -151,14 +160,17 @@ public class MailboxModule {
|
||||
MailboxUpdateManager mailboxUpdateManager,
|
||||
MailboxClientFactory mailboxClientFactory,
|
||||
TorReachabilityMonitor reachabilityMonitor,
|
||||
FeatureFlags featureFlags,
|
||||
LifecycleManager lifecycleManager,
|
||||
EventBus eventBus) {
|
||||
MailboxClientManager manager = new MailboxClientManager(eventExecutor,
|
||||
dbExecutor, db, contactManager, pluginManager,
|
||||
mailboxSettingsManager, mailboxUpdateManager,
|
||||
mailboxClientFactory, reachabilityMonitor);
|
||||
lifecycleManager.registerService(manager);
|
||||
eventBus.addListener(manager);
|
||||
if (featureFlags.shouldEnableMailbox()) {
|
||||
lifecycleManager.registerService(manager);
|
||||
eventBus.addListener(manager);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
@@ -26,7 +25,6 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
||||
private final MailboxApi api;
|
||||
private final MailboxSettingsManager mailboxSettingsManager;
|
||||
private final MailboxUpdateManager mailboxUpdateManager;
|
||||
private final QrCodeClassifier qrCodeClassifier;
|
||||
|
||||
@Inject
|
||||
MailboxPairingTaskFactoryImpl(
|
||||
@@ -36,8 +34,7 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
||||
Clock clock,
|
||||
MailboxApi api,
|
||||
MailboxSettingsManager mailboxSettingsManager,
|
||||
MailboxUpdateManager mailboxUpdateManager,
|
||||
QrCodeClassifier qrCodeClassifier) {
|
||||
MailboxUpdateManager mailboxUpdateManager) {
|
||||
this.eventExecutor = eventExecutor;
|
||||
this.db = db;
|
||||
this.crypto = crypto;
|
||||
@@ -45,13 +42,12 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
||||
this.api = api;
|
||||
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||
this.mailboxUpdateManager = mailboxUpdateManager;
|
||||
this.qrCodeClassifier = qrCodeClassifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailboxPairingTask createPairingTask(String qrCodePayload) {
|
||||
return new MailboxPairingTaskImpl(qrCodePayload, eventExecutor, db,
|
||||
crypto, clock, api, mailboxSettingsManager,
|
||||
mailboxUpdateManager, qrCodeClassifier);
|
||||
mailboxUpdateManager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Consumer;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
@@ -10,26 +9,18 @@ import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pairing;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.QrCodeReceived;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -41,10 +32,7 @@ import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
@@ -52,6 +40,9 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
|
||||
private final static Logger LOG =
|
||||
getLogger(MailboxPairingTaskImpl.class.getName());
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
|
||||
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||
private static final int VERSION_REQUIRED = 32;
|
||||
|
||||
private final String payload;
|
||||
private final Executor eventExecutor;
|
||||
@@ -61,8 +52,6 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
private final MailboxApi api;
|
||||
private final MailboxSettingsManager mailboxSettingsManager;
|
||||
private final MailboxUpdateManager mailboxUpdateManager;
|
||||
private final QrCodeClassifier qrCodeClassifier;
|
||||
private final long timeStarted;
|
||||
|
||||
private final Object lock = new Object();
|
||||
@GuardedBy("lock")
|
||||
@@ -79,8 +68,7 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
Clock clock,
|
||||
MailboxApi api,
|
||||
MailboxSettingsManager mailboxSettingsManager,
|
||||
MailboxUpdateManager mailboxUpdateManager,
|
||||
QrCodeClassifier qrCodeClassifier) {
|
||||
MailboxUpdateManager mailboxUpdateManager) {
|
||||
this.payload = payload;
|
||||
this.eventExecutor = eventExecutor;
|
||||
this.db = db;
|
||||
@@ -89,9 +77,7 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
this.api = api;
|
||||
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||
this.mailboxUpdateManager = mailboxUpdateManager;
|
||||
this.qrCodeClassifier = qrCodeClassifier;
|
||||
timeStarted = clock.currentTimeMillis();
|
||||
state = new QrCodeReceived(timeStarted);
|
||||
state = new MailboxPairingState.QrCodeReceived();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -113,30 +99,22 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Pair<QrCodeType, Integer> typeAndVersion =
|
||||
qrCodeClassifier.classifyQrCode(payload);
|
||||
QrCodeType qrCodeType = typeAndVersion.getFirst();
|
||||
int formatVersion = typeAndVersion.getSecond();
|
||||
if (qrCodeType != MAILBOX || formatVersion != QR_FORMAT_VERSION) {
|
||||
setState(new InvalidQrCode(qrCodeType, formatVersion));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
pairMailbox();
|
||||
} catch (FormatException e) {
|
||||
onMailboxError(e, new InvalidQrCode(qrCodeType, formatVersion));
|
||||
onMailboxError(e, new MailboxPairingState.InvalidQrCode());
|
||||
} catch (MailboxAlreadyPairedException e) {
|
||||
onMailboxError(e, new MailboxAlreadyPaired());
|
||||
onMailboxError(e, new MailboxPairingState.MailboxAlreadyPaired());
|
||||
} catch (IOException e) {
|
||||
onMailboxError(e, new ConnectionError());
|
||||
onMailboxError(e, new MailboxPairingState.ConnectionError());
|
||||
} catch (ApiException | DbException e) {
|
||||
onMailboxError(e, new UnexpectedError());
|
||||
onMailboxError(e, new MailboxPairingState.UnexpectedError());
|
||||
}
|
||||
}
|
||||
|
||||
private void pairMailbox() throws IOException, ApiException, DbException {
|
||||
MailboxProperties mailboxProperties = decodeQrCodePayload(payload);
|
||||
setState(new Pairing(timeStarted));
|
||||
setState(new MailboxPairingState.Pairing());
|
||||
MailboxProperties ownerProperties = api.setup(mailboxProperties);
|
||||
long time = clock.currentTimeMillis();
|
||||
db.transaction(false, txn -> {
|
||||
@@ -155,7 +133,7 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
}
|
||||
}
|
||||
});
|
||||
setState(new Paired());
|
||||
setState(new MailboxPairingState.Paired());
|
||||
}
|
||||
|
||||
private void onMailboxError(Exception e, MailboxPairingState state) {
|
||||
@@ -189,6 +167,14 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
}
|
||||
throw new FormatException();
|
||||
}
|
||||
int version = bytes[0] & 0xFF;
|
||||
if (version != VERSION_REQUIRED) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("QR code has not version " + VERSION_REQUIRED +
|
||||
": " + version);
|
||||
}
|
||||
throw new FormatException();
|
||||
}
|
||||
LOG.info("QR code is valid");
|
||||
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
|
||||
String onion = crypto.encodeOnion(onionPubKey);
|
||||
|
||||
@@ -32,7 +32,6 @@ class TorReachabilityMonitorImpl
|
||||
|
||||
private final Executor ioExecutor;
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final MailboxConfig mailboxConfig;
|
||||
private final PluginManager pluginManager;
|
||||
private final EventBus eventBus;
|
||||
private final Object lock = new Object();
|
||||
@@ -51,12 +50,10 @@ class TorReachabilityMonitorImpl
|
||||
TorReachabilityMonitorImpl(
|
||||
@IoExecutor Executor ioExecutor,
|
||||
TaskScheduler taskScheduler,
|
||||
MailboxConfig mailboxConfig,
|
||||
PluginManager pluginManager,
|
||||
EventBus eventBus) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.mailboxConfig = mailboxConfig;
|
||||
this.pluginManager = pluginManager;
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
@@ -113,7 +110,7 @@ class TorReachabilityMonitorImpl
|
||||
synchronized (lock) {
|
||||
if (destroyed || task != null) return;
|
||||
task = taskScheduler.schedule(this::onTorReachable, ioExecutor,
|
||||
mailboxConfig.getTorReachabilityPeriod(), MILLISECONDS);
|
||||
REACHABILITY_PERIOD_MS, MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,7 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class ModularMailboxModule {
|
||||
@Provides
|
||||
MailboxConfig provideMailboxConfig(MailboxConfigImpl mailboxConfig) {
|
||||
return mailboxConfig;
|
||||
}
|
||||
public class UrlConverterModule {
|
||||
|
||||
@Provides
|
||||
UrlConverter provideUrlConverter(UrlConverterImpl urlConverter) {
|
||||
@@ -40,9 +40,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -288,10 +288,8 @@ class PluginManagerImpl implements PluginManager, Service {
|
||||
private class Callback implements PluginCallback {
|
||||
|
||||
private final TransportId id;
|
||||
private final Object stateLock = new Object();
|
||||
|
||||
@GuardedBy("lock")
|
||||
private State state = STARTING_STOPPING;
|
||||
private final AtomicReference<State> state =
|
||||
new AtomicReference<>(STARTING_STOPPING);
|
||||
|
||||
private Callback(TransportId id) {
|
||||
this.id = id;
|
||||
@@ -345,26 +343,22 @@ class PluginManagerImpl implements PluginManager, Service {
|
||||
|
||||
@Override
|
||||
public void pluginStateChanged(State newState) {
|
||||
synchronized (stateLock) {
|
||||
if (newState != state) {
|
||||
State oldState = state;
|
||||
state = newState;
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info(id + " changed from state " + oldState
|
||||
+ " to " + newState);
|
||||
}
|
||||
eventBus.broadcast(new TransportStateEvent(id, newState));
|
||||
if (newState == ACTIVE) {
|
||||
eventBus.broadcast(new TransportActiveEvent(id));
|
||||
} else if (oldState == ACTIVE) {
|
||||
eventBus.broadcast(new TransportInactiveEvent(id));
|
||||
}
|
||||
} else if (newState == DISABLED) {
|
||||
// Broadcast an event even though the state hasn't changed,
|
||||
// as the reasons for the plugin being disabled may have
|
||||
// changed
|
||||
eventBus.broadcast(new TransportStateEvent(id, newState));
|
||||
State oldState = state.getAndSet(newState);
|
||||
if (newState != oldState) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info(id + " changed from state " + oldState
|
||||
+ " to " + newState);
|
||||
}
|
||||
eventBus.broadcast(new TransportStateEvent(id, newState));
|
||||
if (newState == ACTIVE) {
|
||||
eventBus.broadcast(new TransportActiveEvent(id));
|
||||
} else if (oldState == ACTIVE) {
|
||||
eventBus.broadcast(new TransportInactiveEvent(id));
|
||||
}
|
||||
} else if (newState == DISABLED) {
|
||||
// Broadcast an event even though the state hasn't changed, as
|
||||
// the reasons for the plugin being disabled may have changed
|
||||
eventBus.broadcast(new TransportStateEvent(id, newState));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,17 +89,6 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
|
||||
|
||||
private volatile String contactConnectionsUuid = null;
|
||||
|
||||
/**
|
||||
* Override and return true, if the plugin is now allowed to access the
|
||||
* Bluetooth hardware.
|
||||
* If this returns false, the plugin must be
|
||||
* {@link org.briarproject.bramble.api.plugin.Plugin.State#DISABLED}
|
||||
* in {@link #start()} and not attempt to access Bluetooth hardware.
|
||||
*/
|
||||
protected boolean isBluetoothAccessible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract void initialiseAdapter() throws IOException;
|
||||
|
||||
abstract boolean isAdapterEnabled();
|
||||
@@ -187,28 +176,19 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
|
||||
DEFAULT_PREF_PLUGIN_ENABLE);
|
||||
everConnected.set(settings.getBoolean(PREF_EVER_CONNECTED,
|
||||
DEFAULT_PREF_EVER_CONNECTED));
|
||||
// disable plugin, if conditions for enabling are not met
|
||||
if (enabledByUser && !isBluetoothAccessible()) {
|
||||
enabledByUser = false;
|
||||
settings.putBoolean(PREF_PLUGIN_ENABLE, false);
|
||||
callback.mergeSettings(settings);
|
||||
}
|
||||
state.setStarted(enabledByUser);
|
||||
try {
|
||||
initialiseAdapter();
|
||||
} catch (IOException e) {
|
||||
throw new PluginException(e);
|
||||
}
|
||||
if (enabledByUser) {
|
||||
updateProperties();
|
||||
if (isAdapterEnabled()) bind();
|
||||
}
|
||||
updateProperties();
|
||||
if (enabledByUser && isAdapterEnabled()) bind();
|
||||
}
|
||||
|
||||
private void bind() {
|
||||
ioExecutor.execute(() -> {
|
||||
if (getState() != INACTIVE) return;
|
||||
if (contactConnectionsUuid == null) updateProperties();
|
||||
// Bind a server socket to accept connections from contacts
|
||||
SS ss;
|
||||
try {
|
||||
@@ -554,8 +534,7 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
|
||||
private void onSettingsUpdated(Settings settings) {
|
||||
boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE,
|
||||
DEFAULT_PREF_PLUGIN_ENABLE);
|
||||
boolean shouldEnable = enabledByUser && isBluetoothAccessible();
|
||||
SS ss = state.setEnabledByUser(shouldEnable);
|
||||
SS ss = state.setEnabledByUser(enabledByUser);
|
||||
State s = getState();
|
||||
if (ss != null) {
|
||||
LOG.info("Disabled by user, closing server socket");
|
||||
|
||||
@@ -419,7 +419,7 @@ class LanTcpPlugin extends TcpPlugin {
|
||||
private InetSocketAddress parseSocketAddress(BdfList descriptor)
|
||||
throws FormatException {
|
||||
byte[] address = descriptor.getRaw(1);
|
||||
int port = descriptor.getInt(2);
|
||||
int port = descriptor.getLong(2).intValue();
|
||||
if (port < 1 || port > MAX_16_BIT_UNSIGNED) throw new FormatException();
|
||||
try {
|
||||
InetAddress addr = InetAddress.getByAddress(address);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.onionwrapper;
|
||||
package org.briarproject.bramble.plugin.tor;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.briarproject.bramble.plugin.tor;
|
||||
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface CircumventionProvider {
|
||||
|
||||
enum BridgeType {
|
||||
DEFAULT_OBFS4,
|
||||
NON_DEFAULT_OBFS4,
|
||||
VANILLA,
|
||||
MEEK,
|
||||
SNOWFLAKE
|
||||
}
|
||||
|
||||
/**
|
||||
* Countries where Tor is blocked, i.e. vanilla Tor connection won't work.
|
||||
* <p>
|
||||
* See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
|
||||
* and https://trac.torproject.org/projects/tor/wiki/doc/OONI/censorshipwiki
|
||||
*/
|
||||
String[] BLOCKED = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"};
|
||||
|
||||
/**
|
||||
* Countries where bridge connections are likely to work.
|
||||
* Should be a subset of {@link #BLOCKED} and the union of
|
||||
* {@link #DEFAULT_BRIDGES}, {@link #NON_DEFAULT_BRIDGES} and
|
||||
* {@link #DPI_BRIDGES}.
|
||||
*/
|
||||
String[] BRIDGES = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"};
|
||||
|
||||
/**
|
||||
* Countries where default obfs4 or vanilla bridges are likely to work.
|
||||
* Should be a subset of {@link #BRIDGES}.
|
||||
*/
|
||||
String[] DEFAULT_BRIDGES = {"EG", "VE"};
|
||||
|
||||
/**
|
||||
* Countries where non-default obfs4 or vanilla bridges are likely to work.
|
||||
* Should be a subset of {@link #BRIDGES}.
|
||||
*/
|
||||
String[] NON_DEFAULT_BRIDGES = {"BY", "RU"};
|
||||
|
||||
/**
|
||||
* Countries where vanilla bridges are blocked via DPI but non-default
|
||||
* obfs4 bridges, meek and snowflake may work. Should be a subset of
|
||||
* {@link #BRIDGES}.
|
||||
*/
|
||||
String[] DPI_BRIDGES = {"CN", "IR", "TM"};
|
||||
|
||||
/**
|
||||
* Returns true if vanilla Tor connections are blocked in the given country.
|
||||
*/
|
||||
boolean isTorProbablyBlocked(String countryCode);
|
||||
|
||||
/**
|
||||
* Returns true if bridge connections of some type work in the given
|
||||
* country.
|
||||
*/
|
||||
boolean doBridgesWork(String countryCode);
|
||||
|
||||
/**
|
||||
* Returns the types of bridge connection that are suitable for the given
|
||||
* country, or {@link #DEFAULT_BRIDGES} if no bridge type is known
|
||||
* to work.
|
||||
*/
|
||||
List<BridgeType> getSuitableBridgeTypes(String countryCode);
|
||||
|
||||
@IoExecutor
|
||||
List<String> getBridges(BridgeType type, String countryCode,
|
||||
boolean letsEncrypt);
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package org.briarproject.bramble.plugin.tor;
|
||||
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProviderImpl.SnowflakeBroker.AMP_CACHE;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProviderImpl.SnowflakeBroker.AZURE;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProviderImpl.SnowflakeBroker.FASTLY;
|
||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class CircumventionProviderImpl implements CircumventionProvider {
|
||||
|
||||
private final static String BRIDGE_FILE_NAME = "bridges";
|
||||
private final static String SNOWFLAKE_PARAMS_FILE_NAME = "snowflake-params";
|
||||
private final static String DEFAULT_COUNTRY_CODE = "ZZ";
|
||||
|
||||
private static final Set<String> BLOCKED_IN_COUNTRIES =
|
||||
new HashSet<>(asList(BLOCKED));
|
||||
private static final Set<String> BRIDGE_COUNTRIES =
|
||||
new HashSet<>(asList(BRIDGES));
|
||||
private static final Set<String> DEFAULT_OBFS4_BRIDGE_COUNTRIES =
|
||||
new HashSet<>(asList(DEFAULT_BRIDGES));
|
||||
private static final Set<String> NON_DEFAULT_BRIDGE_COUNTRIES =
|
||||
new HashSet<>(asList(NON_DEFAULT_BRIDGES));
|
||||
private static final Set<String> DPI_COUNTRIES =
|
||||
new HashSet<>(asList(DPI_BRIDGES));
|
||||
|
||||
// Package access for testing
|
||||
enum SnowflakeBroker {
|
||||
|
||||
FASTLY('F'),
|
||||
AZURE('A'),
|
||||
AMP_CACHE('M');
|
||||
|
||||
private final char value;
|
||||
|
||||
SnowflakeBroker(char value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@Inject
|
||||
CircumventionProviderImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTorProbablyBlocked(String countryCode) {
|
||||
return BLOCKED_IN_COUNTRIES.contains(countryCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doBridgesWork(String countryCode) {
|
||||
return BRIDGE_COUNTRIES.contains(countryCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BridgeType> getSuitableBridgeTypes(String countryCode) {
|
||||
if (DEFAULT_OBFS4_BRIDGE_COUNTRIES.contains(countryCode)) {
|
||||
return asList(DEFAULT_OBFS4, VANILLA);
|
||||
} else if (NON_DEFAULT_BRIDGE_COUNTRIES.contains(countryCode)) {
|
||||
return asList(NON_DEFAULT_OBFS4, VANILLA);
|
||||
} else if (DPI_COUNTRIES.contains(countryCode)) {
|
||||
return asList(NON_DEFAULT_OBFS4, MEEK, SNOWFLAKE);
|
||||
} else {
|
||||
return asList(DEFAULT_OBFS4, VANILLA);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@IoExecutor
|
||||
public List<String> getBridges(BridgeType type, String countryCode,
|
||||
boolean letsEncrypt) {
|
||||
InputStream is = requireNonNull(getClass().getClassLoader()
|
||||
.getResourceAsStream(BRIDGE_FILE_NAME));
|
||||
Scanner scanner = new Scanner(is);
|
||||
|
||||
List<String> bridges = new ArrayList<>();
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = scanner.nextLine();
|
||||
if ((type == DEFAULT_OBFS4 && line.startsWith("d ")) ||
|
||||
(type == NON_DEFAULT_OBFS4 && line.startsWith("n ")) ||
|
||||
(type == VANILLA && line.startsWith("v ")) ||
|
||||
(type == MEEK && line.startsWith("m "))) {
|
||||
bridges.add(line.substring(2));
|
||||
} else if (type == SNOWFLAKE && line.startsWith("s ")) {
|
||||
String params;
|
||||
// If the client can verify Let's Encrypt certificates then
|
||||
// use the Fastly broker, otherwise use Azure
|
||||
if (letsEncrypt) {
|
||||
params = getSnowflakeParams(countryCode, FASTLY);
|
||||
} else {
|
||||
params = getSnowflakeParams(countryCode, AZURE);
|
||||
}
|
||||
bridges.add(line.substring(2) + " " + params);
|
||||
// Also use the AMP cache broker
|
||||
params = getSnowflakeParams(countryCode, AMP_CACHE);
|
||||
bridges.add(line.substring(2) + " " + params);
|
||||
}
|
||||
}
|
||||
scanner.close();
|
||||
return bridges;
|
||||
}
|
||||
|
||||
// Package access for testing
|
||||
String getSnowflakeParams(String countryCode, SnowflakeBroker broker) {
|
||||
Map<String, String> params = loadSnowflakeParams();
|
||||
if (countryCode.isEmpty()) countryCode = DEFAULT_COUNTRY_CODE;
|
||||
// If we have parameters for this country code, return them
|
||||
String value = params.get(makeKey(countryCode, broker));
|
||||
if (value != null) return value;
|
||||
// Return the default parameters
|
||||
value = params.get(makeKey(DEFAULT_COUNTRY_CODE, broker));
|
||||
return requireNonNull(value);
|
||||
}
|
||||
|
||||
private Map<String, String> loadSnowflakeParams() {
|
||||
InputStream is = requireNonNull(getClass().getClassLoader()
|
||||
.getResourceAsStream(SNOWFLAKE_PARAMS_FILE_NAME));
|
||||
Scanner scanner = new Scanner(is);
|
||||
Map<String, String> params = new TreeMap<>();
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = scanner.nextLine();
|
||||
if (line.length() < 5) continue;
|
||||
String key = line.substring(0, 4); // Country code, space, broker
|
||||
String value = line.substring(5);
|
||||
params.put(key, value);
|
||||
}
|
||||
scanner.close();
|
||||
return params;
|
||||
}
|
||||
|
||||
private String makeKey(String countryCode, SnowflakeBroker broker) {
|
||||
return countryCode + " " + broker.value;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.briarproject.bramble.plugin.tor;
|
||||
|
||||
import net.freehaven.tor.control.EventHandler;
|
||||
import net.freehaven.tor.control.TorControlConnection;
|
||||
|
||||
import org.briarproject.bramble.PoliteExecutor;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||
@@ -24,27 +27,36 @@ import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
||||
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.nullsafety.InterfaceNotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||
import org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType;
|
||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider.BridgeType;
|
||||
import org.briarproject.onionwrapper.TorWrapper;
|
||||
import org.briarproject.onionwrapper.TorWrapper.HiddenServiceProperties;
|
||||
import org.briarproject.onionwrapper.TorWrapper.Observer;
|
||||
import org.briarproject.onionwrapper.TorWrapper.TorState;
|
||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
@@ -53,9 +65,13 @@ import javax.net.SocketFactory;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
|
||||
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
|
||||
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;
|
||||
@@ -77,19 +93,34 @@ 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.CircumventionProvider.BridgeType.MEEK;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE;
|
||||
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.tryToClose;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.MEEK;
|
||||
import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.SNOWFLAKE;
|
||||
|
||||
@InterfaceNotNullByDefault
|
||||
class TorPlugin implements DuplexPlugin, EventListener {
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
|
||||
protected static final Logger LOG = getLogger(TorPlugin.class.getName());
|
||||
|
||||
private static final String[] EVENTS = {
|
||||
"CIRC",
|
||||
"ORCONN",
|
||||
"STATUS_GENERAL",
|
||||
"STATUS_CLIENT",
|
||||
"HS_DESC",
|
||||
"NOTICE",
|
||||
"WARN",
|
||||
"ERR"
|
||||
};
|
||||
private static final String OWNER = "__OwningControllerProcess";
|
||||
private static final int COOKIE_TIMEOUT_MS = 3000;
|
||||
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
|
||||
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
|
||||
|
||||
protected final Executor ioExecutor;
|
||||
@@ -98,79 +129,91 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
private final NetworkManager networkManager;
|
||||
private final LocationUtils locationUtils;
|
||||
private final SocketFactory torSocketFactory;
|
||||
private final CircumventionProvider circumventionProvider;
|
||||
private final Clock clock;
|
||||
private final BatteryManager batteryManager;
|
||||
private final Backoff backoff;
|
||||
private final TorRendezvousCrypto torRendezvousCrypto;
|
||||
private final TorWrapper tor;
|
||||
private final PluginCallback callback;
|
||||
private final String architecture;
|
||||
private final CircumventionProvider circumventionProvider;
|
||||
private final ResourceProvider resourceProvider;
|
||||
private final long maxLatency;
|
||||
private final int maxIdleTime;
|
||||
private final boolean canVerifyLetsEncryptCerts;
|
||||
private final int socketTimeout;
|
||||
private final File torDirectory, geoIpFile, configFile;
|
||||
private final int torSocksPort;
|
||||
private final int torControlPort;
|
||||
private final File doneFile, cookieFile;
|
||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||
|
||||
protected final PluginState state = new PluginState();
|
||||
|
||||
private volatile Socket controlSocket = null;
|
||||
private volatile TorControlConnection controlConnection = null;
|
||||
private volatile Settings settings = null;
|
||||
|
||||
protected abstract int getProcessId();
|
||||
|
||||
protected abstract long getLastUpdateTime();
|
||||
|
||||
TorPlugin(Executor ioExecutor,
|
||||
Executor wakefulIoExecutor,
|
||||
NetworkManager networkManager,
|
||||
LocationUtils locationUtils,
|
||||
SocketFactory torSocketFactory,
|
||||
Clock clock,
|
||||
ResourceProvider resourceProvider,
|
||||
CircumventionProvider circumventionProvider,
|
||||
BatteryManager batteryManager,
|
||||
Backoff backoff,
|
||||
TorRendezvousCrypto torRendezvousCrypto,
|
||||
TorWrapper tor,
|
||||
PluginCallback callback,
|
||||
String architecture,
|
||||
long maxLatency,
|
||||
int maxIdleTime,
|
||||
boolean canVerifyLetsEncryptCerts) {
|
||||
File torDirectory,
|
||||
int torSocksPort,
|
||||
int torControlPort) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.wakefulIoExecutor = wakefulIoExecutor;
|
||||
this.networkManager = networkManager;
|
||||
this.locationUtils = locationUtils;
|
||||
this.torSocketFactory = torSocketFactory;
|
||||
this.clock = clock;
|
||||
this.resourceProvider = resourceProvider;
|
||||
this.circumventionProvider = circumventionProvider;
|
||||
this.batteryManager = batteryManager;
|
||||
this.backoff = backoff;
|
||||
this.torRendezvousCrypto = torRendezvousCrypto;
|
||||
this.tor = tor;
|
||||
this.callback = callback;
|
||||
this.architecture = architecture;
|
||||
this.maxLatency = maxLatency;
|
||||
this.maxIdleTime = maxIdleTime;
|
||||
this.canVerifyLetsEncryptCerts = canVerifyLetsEncryptCerts;
|
||||
if (maxIdleTime > Integer.MAX_VALUE / 2) {
|
||||
if (maxIdleTime > Integer.MAX_VALUE / 2)
|
||||
socketTimeout = Integer.MAX_VALUE;
|
||||
} else {
|
||||
socketTimeout = maxIdleTime * 2;
|
||||
}
|
||||
else socketTimeout = maxIdleTime * 2;
|
||||
this.torDirectory = torDirectory;
|
||||
this.torSocksPort = torSocksPort;
|
||||
this.torControlPort = torControlPort;
|
||||
geoIpFile = new File(torDirectory, "geoip");
|
||||
configFile = new File(torDirectory, "torrc");
|
||||
doneFile = new File(torDirectory, "done");
|
||||
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
||||
// Don't execute more than one connection status check at a time
|
||||
connectionStatusExecutor =
|
||||
new PoliteExecutor("TorPlugin", ioExecutor, 1);
|
||||
tor.setObserver(new Observer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onState(TorState torState) {
|
||||
State s = state.getState(torState);
|
||||
if (s == ACTIVE) backoff.reset();
|
||||
callback.pluginStateChanged(s);
|
||||
}
|
||||
protected File getTorExecutableFile() {
|
||||
return new File(torDirectory, "tor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBootstrapPercentage(int percentage) {
|
||||
}
|
||||
protected File getObfs4ExecutableFile() {
|
||||
return new File(torDirectory, "obfs4proxy");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHsDescriptorUpload(String onion) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClockSkewDetected(long skewSeconds) {
|
||||
}
|
||||
});
|
||||
protected File getSnowflakeExecutableFile() {
|
||||
return new File(torDirectory, "snowflake");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -191,18 +234,89 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
@Override
|
||||
public void start() throws PluginException {
|
||||
if (used.getAndSet(true)) throw new IllegalStateException();
|
||||
if (!torDirectory.exists()) {
|
||||
if (!torDirectory.mkdirs()) {
|
||||
LOG.warning("Could not create Tor directory.");
|
||||
throw new PluginException();
|
||||
}
|
||||
}
|
||||
// Load the settings
|
||||
settings = callback.getSettings();
|
||||
// Start Tor
|
||||
try {
|
||||
tor.start();
|
||||
// Install or update the assets if necessary
|
||||
if (!assetsAreUpToDate()) installAssets();
|
||||
// Start from the default config every time
|
||||
extract(getConfigInputStream(), configFile);
|
||||
} catch (IOException e) {
|
||||
throw new PluginException(e);
|
||||
}
|
||||
if (cookieFile.exists() && !cookieFile.delete())
|
||||
LOG.warning("Old auth cookie not deleted");
|
||||
// Start a new Tor process
|
||||
LOG.info("Starting Tor");
|
||||
File torFile = getTorExecutableFile();
|
||||
String torPath = torFile.getAbsolutePath();
|
||||
String configPath = configFile.getAbsolutePath();
|
||||
String pid = String.valueOf(getProcessId());
|
||||
Process torProcess;
|
||||
ProcessBuilder pb =
|
||||
new ProcessBuilder(torPath, "-f", configPath, OWNER, pid);
|
||||
Map<String, String> env = pb.environment();
|
||||
env.put("HOME", torDirectory.getAbsolutePath());
|
||||
pb.directory(torDirectory);
|
||||
pb.redirectErrorStream(true);
|
||||
try {
|
||||
torProcess = pb.start();
|
||||
} catch (SecurityException | IOException e) {
|
||||
throw new PluginException(e);
|
||||
}
|
||||
try {
|
||||
// Wait for the Tor process to start
|
||||
waitForTorToStart(torProcess);
|
||||
// Wait for the auth cookie file to be created/updated
|
||||
long start = clock.currentTimeMillis();
|
||||
while (cookieFile.length() < 32) {
|
||||
if (clock.currentTimeMillis() - start > COOKIE_TIMEOUT_MS) {
|
||||
LOG.warning("Auth cookie not created");
|
||||
if (LOG.isLoggable(INFO)) listFiles(torDirectory);
|
||||
throw new PluginException();
|
||||
}
|
||||
//noinspection BusyWait
|
||||
Thread.sleep(COOKIE_POLLING_INTERVAL_MS);
|
||||
}
|
||||
LOG.info("Auth cookie created");
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warning("Interrupted while starting Tor");
|
||||
Thread.currentThread().interrupt();
|
||||
throw new PluginException();
|
||||
}
|
||||
try {
|
||||
// Open a control connection and authenticate using the cookie file
|
||||
controlSocket = new Socket("127.0.0.1", torControlPort);
|
||||
controlConnection = new TorControlConnection(controlSocket);
|
||||
controlConnection.authenticate(read(cookieFile));
|
||||
// Tell Tor to exit when the control connection is closed
|
||||
controlConnection.takeOwnership();
|
||||
controlConnection.resetConf(singletonList(OWNER));
|
||||
// Register to receive events from the Tor process
|
||||
controlConnection.setEventHandler(this);
|
||||
controlConnection.setEvents(asList(EVENTS));
|
||||
// Check whether Tor has already bootstrapped
|
||||
String info = controlConnection.getInfo("status/bootstrap-phase");
|
||||
if (info != null && info.contains("PROGRESS=100")) {
|
||||
LOG.info("Tor has already bootstrapped");
|
||||
state.setBootstrapped();
|
||||
}
|
||||
// Check whether Tor has already built a circuit
|
||||
info = controlConnection.getInfo("status/circuit-established");
|
||||
if ("1".equals(info)) {
|
||||
LOG.info("Tor has already built a circuit");
|
||||
state.setCircuitBuilt(true);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PluginException(e);
|
||||
}
|
||||
state.setStarted();
|
||||
// Check whether we're online
|
||||
updateConnectionStatus(networkManager.getNetworkStatus(),
|
||||
batteryManager.isCharging());
|
||||
@@ -210,6 +324,148 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
bind();
|
||||
}
|
||||
|
||||
private boolean assetsAreUpToDate() {
|
||||
return doneFile.lastModified() > getLastUpdateTime();
|
||||
}
|
||||
|
||||
private void installAssets() throws IOException {
|
||||
// The done file may already exist from a previous installation
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
doneFile.delete();
|
||||
// The GeoIP file may exist from a previous installation - we can
|
||||
// save some space by deleting it.
|
||||
// TODO: Remove after a reasonable migration period
|
||||
// (added 2022-03-29)
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
geoIpFile.delete();
|
||||
installTorExecutable();
|
||||
installObfs4Executable();
|
||||
installSnowflakeExecutable();
|
||||
if (!doneFile.createNewFile())
|
||||
LOG.warning("Failed to create done file");
|
||||
}
|
||||
|
||||
protected void extract(InputStream in, File dest) throws IOException {
|
||||
OutputStream out = new FileOutputStream(dest);
|
||||
copyAndClose(in, out);
|
||||
}
|
||||
|
||||
protected void installTorExecutable() throws IOException {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Installing Tor binary for " + architecture);
|
||||
File torFile = getTorExecutableFile();
|
||||
extract(getTorInputStream(), torFile);
|
||||
if (!torFile.setExecutable(true, true)) throw new IOException();
|
||||
}
|
||||
|
||||
protected void installObfs4Executable() throws IOException {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Installing obfs4proxy binary for " + architecture);
|
||||
File obfs4File = getObfs4ExecutableFile();
|
||||
extract(getObfs4InputStream(), obfs4File);
|
||||
if (!obfs4File.setExecutable(true, true)) throw new IOException();
|
||||
}
|
||||
|
||||
protected void installSnowflakeExecutable() throws IOException {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Installing snowflake binary for " + architecture);
|
||||
File snowflakeFile = getSnowflakeExecutableFile();
|
||||
extract(getSnowflakeInputStream(), snowflakeFile);
|
||||
if (!snowflakeFile.setExecutable(true, true)) throw new IOException();
|
||||
}
|
||||
|
||||
private InputStream getTorInputStream() throws IOException {
|
||||
return getZipInputStream("tor");
|
||||
}
|
||||
|
||||
private InputStream getObfs4InputStream() throws IOException {
|
||||
return getZipInputStream("obfs4proxy");
|
||||
}
|
||||
|
||||
private InputStream getSnowflakeInputStream() throws IOException {
|
||||
return getZipInputStream("snowflake");
|
||||
}
|
||||
|
||||
private InputStream getZipInputStream(String basename) throws IOException {
|
||||
InputStream in = resourceProvider
|
||||
.getResourceInputStream(basename + "_" + architecture, ".zip");
|
||||
ZipInputStream zin = new ZipInputStream(in);
|
||||
if (zin.getNextEntry() == null) throw new IOException();
|
||||
return zin;
|
||||
}
|
||||
|
||||
private static void append(StringBuilder strb, String name, Object value) {
|
||||
strb.append(name);
|
||||
strb.append(" ");
|
||||
strb.append(value);
|
||||
strb.append("\n");
|
||||
}
|
||||
|
||||
private InputStream getConfigInputStream() {
|
||||
File dataDirectory = new File(torDirectory, ".tor");
|
||||
StringBuilder strb = new StringBuilder();
|
||||
append(strb, "ControlPort", torControlPort);
|
||||
append(strb, "CookieAuthentication", 1);
|
||||
append(strb, "DataDirectory", dataDirectory.getAbsolutePath());
|
||||
append(strb, "DisableNetwork", 1);
|
||||
append(strb, "RunAsDaemon", 1);
|
||||
append(strb, "SafeSocks", 1);
|
||||
append(strb, "SocksPort", torSocksPort);
|
||||
strb.append("GeoIPFile\n");
|
||||
strb.append("GeoIPv6File\n");
|
||||
append(strb, "ConnectionPadding", 0);
|
||||
String obfs4Path = getObfs4ExecutableFile().getAbsolutePath();
|
||||
append(strb, "ClientTransportPlugin obfs4 exec", obfs4Path);
|
||||
append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path);
|
||||
String snowflakePath = getSnowflakeExecutableFile().getAbsolutePath();
|
||||
append(strb, "ClientTransportPlugin snowflake exec", snowflakePath);
|
||||
//noinspection CharsetObjectCanBeUsed
|
||||
return new ByteArrayInputStream(
|
||||
strb.toString().getBytes(Charset.forName("UTF-8")));
|
||||
}
|
||||
|
||||
private void listFiles(File f) {
|
||||
if (f.isDirectory()) {
|
||||
File[] children = f.listFiles();
|
||||
if (children != null) for (File child : children) listFiles(child);
|
||||
} else {
|
||||
LOG.info(f.getAbsolutePath() + " " + f.length());
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] read(File f) throws IOException {
|
||||
byte[] b = new byte[(int) f.length()];
|
||||
FileInputStream in = new FileInputStream(f);
|
||||
try {
|
||||
int offset = 0;
|
||||
while (offset < b.length) {
|
||||
int read = in.read(b, offset, b.length - offset);
|
||||
if (read == -1) throw new EOFException();
|
||||
offset += read;
|
||||
}
|
||||
return b;
|
||||
} finally {
|
||||
tryToClose(in, LOG, WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
protected void waitForTorToStart(Process torProcess)
|
||||
throws InterruptedException, PluginException {
|
||||
Scanner stdout = new Scanner(torProcess.getInputStream());
|
||||
// Log the first line of stdout (contains Tor and library versions)
|
||||
if (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
||||
// Read the process's stdout (and redirected stderr) until it detaches
|
||||
while (stdout.hasNextLine()) stdout.nextLine();
|
||||
stdout.close();
|
||||
// Wait for the process to detach or exit
|
||||
int exit = torProcess.waitFor();
|
||||
if (exit != 0) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.warning("Tor exited with value " + exit);
|
||||
throw new PluginException();
|
||||
}
|
||||
}
|
||||
|
||||
private void bind() {
|
||||
ioExecutor.execute(() -> {
|
||||
// If there's already a port number stored in config, reuse it
|
||||
@@ -233,9 +489,9 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
return;
|
||||
}
|
||||
// Store the port number
|
||||
int localPort = ss.getLocalPort();
|
||||
String localPort = String.valueOf(ss.getLocalPort());
|
||||
Settings s = new Settings();
|
||||
s.put(PREF_TOR_PORT, String.valueOf(localPort));
|
||||
s.put(PREF_TOR_PORT, localPort);
|
||||
callback.mergeSettings(s);
|
||||
// Create a hidden service if necessary
|
||||
ioExecutor.execute(() -> publishHiddenService(localPort));
|
||||
@@ -245,28 +501,48 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
});
|
||||
}
|
||||
|
||||
private void publishHiddenService(int localPort) {
|
||||
if (!tor.isTorRunning()) return;
|
||||
String privKey = settings.get(HS_PRIVATE_KEY_V3);
|
||||
private void publishHiddenService(String port) {
|
||||
if (!state.isTorRunning()) return;
|
||||
String privKey3 = settings.get(HS_PRIVATE_KEY_V3);
|
||||
publishV3HiddenService(port, privKey3);
|
||||
}
|
||||
|
||||
private void publishV3HiddenService(String port, @Nullable String privKey) {
|
||||
LOG.info("Creating v3 hidden service");
|
||||
HiddenServiceProperties hsProps;
|
||||
Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port);
|
||||
Map<String, String> response;
|
||||
try {
|
||||
hsProps = tor.publishHiddenService(localPort, 80, privKey);
|
||||
// Use the control connection to set up the hidden service
|
||||
if (privKey == null) {
|
||||
response = controlConnection.addOnion("NEW:ED25519-V3",
|
||||
portLines, null);
|
||||
} else {
|
||||
response = controlConnection.addOnion(privKey, portLines);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
return;
|
||||
}
|
||||
if (!response.containsKey(HS_ADDRESS)) {
|
||||
LOG.warning("Tor did not return a hidden service address");
|
||||
return;
|
||||
}
|
||||
if (privKey == null && !response.containsKey(HS_PRIVKEY)) {
|
||||
LOG.warning("Tor did not return a private key");
|
||||
return;
|
||||
}
|
||||
String onion3 = response.get(HS_ADDRESS);
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("V3 hidden service " + scrubOnion(hsProps.onion));
|
||||
LOG.info("V3 hidden service " + scrubOnion(onion3));
|
||||
}
|
||||
if (privKey == null) {
|
||||
// Publish the hidden service's onion hostname in transport props
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put(PROP_ONION_V3, hsProps.onion);
|
||||
p.put(PROP_ONION_V3, onion3);
|
||||
callback.mergeLocalProperties(p);
|
||||
// Save the hidden service's private key for next time
|
||||
Settings s = new Settings();
|
||||
s.put(HS_PRIVATE_KEY_V3, hsProps.privKey);
|
||||
s.put(HS_PRIVATE_KEY_V3, response.get(HS_PRIVKEY));
|
||||
callback.mergeSettings(s);
|
||||
}
|
||||
}
|
||||
@@ -289,28 +565,50 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
protected void enableNetwork(boolean enable) throws IOException {
|
||||
if (!state.enableNetwork(enable)) return; // Unchanged
|
||||
controlConnection.setConf("DisableNetwork", enable ? "0" : "1");
|
||||
}
|
||||
|
||||
private void enableBridges(List<BridgeType> bridgeTypes, String countryCode)
|
||||
throws IOException {
|
||||
if (!state.setBridgeTypes(bridgeTypes)) return; // Unchanged
|
||||
if (bridgeTypes.isEmpty()) {
|
||||
tor.disableBridges();
|
||||
controlConnection.setConf("UseBridges", "0");
|
||||
controlConnection.resetConf(singletonList("Bridge"));
|
||||
} else {
|
||||
List<String> bridges = new ArrayList<>();
|
||||
Collection<String> conf = new ArrayList<>();
|
||||
conf.add("UseBridges 1");
|
||||
boolean letsEncrypt = canVerifyLetsEncryptCerts();
|
||||
for (BridgeType bridgeType : bridgeTypes) {
|
||||
bridges.addAll(circumventionProvider.getBridges(bridgeType,
|
||||
countryCode, canVerifyLetsEncryptCerts));
|
||||
conf.addAll(circumventionProvider
|
||||
.getBridges(bridgeType, countryCode, letsEncrypt));
|
||||
}
|
||||
tor.enableBridges(bridges);
|
||||
controlConnection.setConf(conf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this device can verify Let's Encrypt certificates signed
|
||||
* with the IdentTrust DST Root X3 certificate, which expired at the end of
|
||||
* September 2021.
|
||||
*/
|
||||
protected boolean canVerifyLetsEncryptCerts() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
ServerSocket ss = state.setStopped();
|
||||
tryToClose(ss, LOG, WARNING);
|
||||
try {
|
||||
tor.stop();
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
if (controlSocket != null && controlConnection != null) {
|
||||
try {
|
||||
LOG.info("Stopping Tor");
|
||||
controlConnection.shutdownTor("TERM");
|
||||
controlSocket.close();
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,7 +719,6 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
TransportProperties remoteProperties = new TransportProperties();
|
||||
remoteProperties.put(PROP_ONION_V3, remoteOnion);
|
||||
try {
|
||||
@SuppressWarnings("resource")
|
||||
ServerSocket ss = new ServerSocket();
|
||||
ss.bind(new InetSocketAddress("127.0.0.1", 0));
|
||||
int port = ss.getLocalPort();
|
||||
@@ -438,7 +735,9 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
LOG.info("Rendezvous server socket closed");
|
||||
}
|
||||
});
|
||||
tor.publishHiddenService(port, 80, blob);
|
||||
Map<Integer, String> portLines =
|
||||
singletonMap(80, "127.0.0.1:" + port);
|
||||
controlConnection.addOnion(blob, portLines);
|
||||
return new RendezvousEndpoint() {
|
||||
|
||||
@Override
|
||||
@@ -448,11 +747,8 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
tor.removeHiddenService(localOnion);
|
||||
} finally {
|
||||
tryToClose(ss, LOG, WARNING);
|
||||
}
|
||||
controlConnection.delOnion(localOnion);
|
||||
tryToClose(ss, LOG, WARNING);
|
||||
}
|
||||
};
|
||||
} catch (IOException e) {
|
||||
@@ -461,6 +757,120 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void circuitStatus(String status, String id, String path) {
|
||||
// In case of races between receiving CIRCUIT_ESTABLISHED and setting
|
||||
// DisableNetwork, set our circuitBuilt flag if not already set
|
||||
if (status.equals("BUILT") && state.setCircuitBuilt(true)) {
|
||||
LOG.info("Circuit built");
|
||||
backoff.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void streamStatus(String status, String id, String target) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orConnStatus(String status, String orName) {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status);
|
||||
|
||||
if (status.equals("CONNECTED")) state.onOrConnectionConnected();
|
||||
else if (status.equals("CLOSED")) state.onOrConnectionClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bandwidthUsed(long read, long written) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newDescriptors(List<String> orList) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void message(String severity, String msg) {
|
||||
if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unrecognized(String type, String msg) {
|
||||
if (type.equals("STATUS_CLIENT")) {
|
||||
handleClientStatus(removeSeverity(msg));
|
||||
} else if (type.equals("STATUS_GENERAL")) {
|
||||
handleGeneralStatus(removeSeverity(msg));
|
||||
} else if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) {
|
||||
String[] parts = msg.split(" ");
|
||||
if (parts.length < 2) {
|
||||
LOG.warning("Failed to parse HS_DESC UPLOADED event");
|
||||
} else if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("V3 descriptor uploaded for " + scrubOnion(parts[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controlConnectionClosed() {
|
||||
if (state.isTorRunning()) {
|
||||
throw new RuntimeException("Control connection closed");
|
||||
}
|
||||
}
|
||||
|
||||
private String removeSeverity(String msg) {
|
||||
return msg.replaceFirst("[^ ]+ ", "");
|
||||
}
|
||||
|
||||
private void handleClientStatus(String msg) {
|
||||
if (msg.startsWith("BOOTSTRAP PROGRESS=100")) {
|
||||
LOG.info("Bootstrapped");
|
||||
state.setBootstrapped();
|
||||
backoff.reset();
|
||||
} else if (msg.startsWith("CIRCUIT_ESTABLISHED")) {
|
||||
if (state.setCircuitBuilt(true)) {
|
||||
LOG.info("Circuit built");
|
||||
backoff.reset();
|
||||
}
|
||||
} else if (msg.startsWith("CIRCUIT_NOT_ESTABLISHED")) {
|
||||
if (state.setCircuitBuilt(false)) {
|
||||
LOG.info("Circuit not built");
|
||||
// TODO: Disable and re-enable network to prompt Tor to rebuild
|
||||
// its guard/bridge connections? This will also close any
|
||||
// established circuits, which might still be functioning
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGeneralStatus(String msg) {
|
||||
if (msg.startsWith("CLOCK_JUMPED")) {
|
||||
Long time = parseLongArgument(msg, "TIME");
|
||||
if (time != null && LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Clock jumped " + time + " seconds");
|
||||
}
|
||||
} else if (msg.startsWith("CLOCK_SKEW")) {
|
||||
Long skew = parseLongArgument(msg, "SKEW");
|
||||
if (skew != null && LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Clock is skewed by " + skew + " seconds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Long parseLongArgument(String msg, String argName) {
|
||||
String[] args = msg.split(" ");
|
||||
for (String arg : args) {
|
||||
if (arg.startsWith(argName + "=")) {
|
||||
try {
|
||||
return Long.parseLong(arg.substring(argName.length() + 1));
|
||||
} catch (NumberFormatException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Failed to parse " + argName + " from '" + msg + "'");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof SettingsUpdatedEvent) {
|
||||
@@ -483,7 +893,7 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
private void updateConnectionStatus(NetworkStatus status,
|
||||
boolean charging) {
|
||||
connectionStatusExecutor.execute(() -> {
|
||||
if (!tor.isTorRunning()) return;
|
||||
if (!state.isTorRunning()) return;
|
||||
boolean online = status.isConnected();
|
||||
boolean wifi = status.isWifi();
|
||||
boolean ipv6Only = status.isIpv6Only();
|
||||
@@ -567,22 +977,41 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
try {
|
||||
if (enableNetwork) {
|
||||
enableBridges(bridgeTypes, country);
|
||||
tor.enableConnectionPadding(enableConnectionPadding);
|
||||
tor.enableIpv6(ipv6Only);
|
||||
enableConnectionPadding(enableConnectionPadding);
|
||||
enableIpv6(ipv6Only);
|
||||
}
|
||||
tor.enableNetwork(enableNetwork);
|
||||
enableNetwork(enableNetwork);
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void enableConnectionPadding(boolean enable) throws IOException {
|
||||
if (!state.enableConnectionPadding(enable)) return; // Unchanged
|
||||
controlConnection.setConf("ConnectionPadding", enable ? "1" : "0");
|
||||
}
|
||||
|
||||
private void enableIpv6(boolean enable) throws IOException {
|
||||
if (!state.enableIpv6(enable)) return; // Unchanged
|
||||
controlConnection.setConf("ClientUseIPv4", enable ? "0" : "1");
|
||||
controlConnection.setConf("ClientUseIPv6", enable ? "1" : "0");
|
||||
}
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
private class PluginState {
|
||||
protected class PluginState {
|
||||
|
||||
@GuardedBy("this")
|
||||
private boolean settingsChecked = false;
|
||||
private boolean started = false,
|
||||
stopped = false,
|
||||
networkInitialised = false,
|
||||
networkEnabled = false,
|
||||
paddingEnabled = false,
|
||||
ipv6Enabled = false,
|
||||
bootstrapped = false,
|
||||
circuitBuilt = false,
|
||||
settingsChecked = false;
|
||||
|
||||
@GuardedBy("this")
|
||||
private int reasonsDisabled = 0;
|
||||
@@ -591,13 +1020,84 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
@Nullable
|
||||
private ServerSocket serverSocket = null;
|
||||
|
||||
@GuardedBy("this")
|
||||
private int orConnectionsConnected = 0;
|
||||
|
||||
@GuardedBy("this")
|
||||
private List<BridgeType> bridgeTypes = emptyList();
|
||||
|
||||
private synchronized void setStarted() {
|
||||
started = true;
|
||||
callback.pluginStateChanged(getState());
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private synchronized boolean isTorRunning() {
|
||||
return started && !stopped;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private synchronized ServerSocket setStopped() {
|
||||
stopped = true;
|
||||
ServerSocket ss = serverSocket;
|
||||
serverSocket = null;
|
||||
callback.pluginStateChanged(getState());
|
||||
return ss;
|
||||
}
|
||||
|
||||
private synchronized void setBootstrapped() {
|
||||
boolean wasBootstrapped = bootstrapped;
|
||||
bootstrapped = true;
|
||||
if (!wasBootstrapped) callback.pluginStateChanged(getState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the `circuitBuilt` flag and returns true if the flag has
|
||||
* changed.
|
||||
*/
|
||||
private synchronized boolean setCircuitBuilt(boolean built) {
|
||||
if (built == circuitBuilt) return false; // Unchanged
|
||||
circuitBuilt = built;
|
||||
callback.pluginStateChanged(getState());
|
||||
return true; // Changed
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the `networkEnabled` flag and returns true if the flag has
|
||||
* changed.
|
||||
*/
|
||||
private synchronized boolean enableNetwork(boolean enable) {
|
||||
boolean wasInitialised = networkInitialised;
|
||||
boolean wasEnabled = networkEnabled;
|
||||
networkInitialised = true;
|
||||
networkEnabled = enable;
|
||||
if (!enable) circuitBuilt = false;
|
||||
if (!wasInitialised || enable != wasEnabled) {
|
||||
callback.pluginStateChanged(getState());
|
||||
}
|
||||
return enable != wasEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the `paddingEnabled` flag and returns true if the flag has
|
||||
* changed. Doesn't affect getState().
|
||||
*/
|
||||
private synchronized boolean enableConnectionPadding(boolean enable) {
|
||||
if (enable == paddingEnabled) return false; // Unchanged
|
||||
paddingEnabled = enable;
|
||||
return true; // Changed
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the `ipv6Enabled` flag and returns true if the flag has
|
||||
* changed. Doesn't affect getState().
|
||||
*/
|
||||
private synchronized boolean enableIpv6(boolean enable) {
|
||||
if (enable == ipv6Enabled) return false; // Unchanged
|
||||
ipv6Enabled = enable;
|
||||
return true; // Changed
|
||||
}
|
||||
|
||||
private synchronized void setReasonsDisabled(int reasons) {
|
||||
boolean wasChecked = settingsChecked;
|
||||
settingsChecked = true;
|
||||
@@ -610,7 +1110,7 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
|
||||
// Doesn't affect getState()
|
||||
private synchronized boolean setServerSocket(ServerSocket ss) {
|
||||
if (serverSocket != null || !tor.isTorRunning()) return false;
|
||||
if (stopped || serverSocket != null) return false;
|
||||
serverSocket = ss;
|
||||
return true;
|
||||
}
|
||||
@@ -620,22 +1120,57 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
||||
if (serverSocket == ss) serverSocket = null;
|
||||
}
|
||||
|
||||
private synchronized State getState() {
|
||||
return getState(tor.getTorState());
|
||||
/**
|
||||
* Sets the list of bridge types being used and returns true if the
|
||||
* list has changed. The list is empty if bridges are disabled.
|
||||
* Doesn't affect getState().
|
||||
*/
|
||||
private synchronized boolean setBridgeTypes(List<BridgeType> types) {
|
||||
if (types.equals(bridgeTypes)) return false; // Unchanged
|
||||
bridgeTypes = types;
|
||||
return true; // Changed
|
||||
}
|
||||
|
||||
private synchronized State getState(TorState torState) {
|
||||
if (torState == TorState.STARTING_STOPPING || !settingsChecked) {
|
||||
private synchronized State getState() {
|
||||
if (!started || stopped || !settingsChecked) {
|
||||
return STARTING_STOPPING;
|
||||
}
|
||||
if (reasonsDisabled != 0) return DISABLED;
|
||||
if (torState == TorState.CONNECTING) return ENABLING;
|
||||
if (torState == TorState.CONNECTED) return ACTIVE;
|
||||
return INACTIVE;
|
||||
if (!networkInitialised) return ENABLING;
|
||||
if (!networkEnabled) return INACTIVE;
|
||||
return bootstrapped && circuitBuilt && orConnectionsConnected > 0
|
||||
? ACTIVE : ENABLING;
|
||||
}
|
||||
|
||||
private synchronized int getReasonsDisabled() {
|
||||
return getState() == DISABLED ? reasonsDisabled : 0;
|
||||
}
|
||||
|
||||
private synchronized void onOrConnectionConnected() {
|
||||
int oldConnected = orConnectionsConnected;
|
||||
orConnectionsConnected++;
|
||||
logOrConnections();
|
||||
if (oldConnected == 0) callback.pluginStateChanged(getState());
|
||||
}
|
||||
|
||||
private synchronized void onOrConnectionClosed() {
|
||||
int oldConnected = orConnectionsConnected;
|
||||
orConnectionsConnected--;
|
||||
if (orConnectionsConnected < 0) {
|
||||
LOG.warning("Count was zero before connection closed");
|
||||
orConnectionsConnected = 0;
|
||||
}
|
||||
logOrConnections();
|
||||
if (orConnectionsConnected == 0 && oldConnected != 0) {
|
||||
callback.pluginStateChanged(getState());
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private void logOrConnections() {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info(orConnectionsConnected + " OR connections connected");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.tor;
|
||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.network.NetworkManager;
|
||||
import org.briarproject.bramble.api.plugin.Backoff;
|
||||
@@ -18,9 +17,9 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -46,12 +45,13 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
||||
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
|
||||
private static final double BACKOFF_BASE = 1.2;
|
||||
|
||||
protected final Executor ioExecutor, eventExecutor, wakefulIoExecutor;
|
||||
protected final Executor ioExecutor, wakefulIoExecutor;
|
||||
protected final NetworkManager networkManager;
|
||||
protected final LocationUtils locationUtils;
|
||||
protected final EventBus eventBus;
|
||||
protected final SocketFactory torSocketFactory;
|
||||
protected final BackoffFactory backoffFactory;
|
||||
protected final ResourceProvider resourceProvider;
|
||||
protected final CircumventionProvider circumventionProvider;
|
||||
protected final BatteryManager batteryManager;
|
||||
protected final Clock clock;
|
||||
@@ -61,13 +61,13 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
||||
protected final int torControlPort;
|
||||
|
||||
TorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||
@EventExecutor Executor eventExecutor,
|
||||
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||
NetworkManager networkManager,
|
||||
LocationUtils locationUtils,
|
||||
EventBus eventBus,
|
||||
SocketFactory torSocketFactory,
|
||||
BackoffFactory backoffFactory,
|
||||
ResourceProvider resourceProvider,
|
||||
CircumventionProvider circumventionProvider,
|
||||
BatteryManager batteryManager,
|
||||
Clock clock,
|
||||
@@ -76,13 +76,13 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
||||
@TorSocksPort int torSocksPort,
|
||||
@TorControlPort int torControlPort) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.eventExecutor = eventExecutor;
|
||||
this.wakefulIoExecutor = wakefulIoExecutor;
|
||||
this.networkManager = networkManager;
|
||||
this.locationUtils = locationUtils;
|
||||
this.eventBus = eventBus;
|
||||
this.torSocketFactory = torSocketFactory;
|
||||
this.backoffFactory = backoffFactory;
|
||||
this.resourceProvider = resourceProvider;
|
||||
this.circumventionProvider = circumventionProvider;
|
||||
this.batteryManager = batteryManager;
|
||||
this.clock = clock;
|
||||
|
||||
@@ -7,7 +7,7 @@ import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
|
||||
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
|
||||
|
||||
@@ -31,6 +31,6 @@ class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
|
||||
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
|
||||
byte[] hash = spec.getH();
|
||||
byte[] base64 = Base64.encode(hash);
|
||||
return "ED25519-V3:" + new String(base64, US_ASCII);
|
||||
return "ED25519-V3:" + new String(base64, Charset.forName("US-ASCII"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
// Retrieve and parse the latest local properties
|
||||
for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) {
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
e.getValue().messageId, false);
|
||||
e.getValue().messageId);
|
||||
local.put(e.getKey(), parseProperties(message));
|
||||
}
|
||||
return local;
|
||||
@@ -222,7 +222,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
if (latest != null) {
|
||||
// Retrieve and parse the latest local properties
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId, false);
|
||||
latest.messageId);
|
||||
p = parseProperties(message);
|
||||
}
|
||||
return p == null ? new TransportProperties() : p;
|
||||
@@ -252,7 +252,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
local = new TransportProperties();
|
||||
} else {
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId, false);
|
||||
latest.messageId);
|
||||
local = parseProperties(message);
|
||||
}
|
||||
storeLocalProperties(txn, c, t, local);
|
||||
@@ -272,8 +272,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
remote = new TransportProperties();
|
||||
} else {
|
||||
// Retrieve and parse the latest remote properties
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId, false);
|
||||
BdfList message =
|
||||
clientHelper.getMessageAsList(txn, latest.messageId);
|
||||
remote = parseProperties(message);
|
||||
}
|
||||
// Merge in any discovered properties
|
||||
@@ -317,7 +317,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
changed = true;
|
||||
} else {
|
||||
BdfList message = clientHelper.getMessageAsList(txn,
|
||||
latest.messageId, false);
|
||||
latest.messageId);
|
||||
TransportProperties old = parseProperties(message);
|
||||
merged = new TransportProperties(old);
|
||||
for (Entry<String, String> e : p.entrySet()) {
|
||||
|
||||
@@ -27,10 +27,7 @@ class TransportPropertyValidator extends BdfMessageValidator {
|
||||
|
||||
TransportPropertyValidator(ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
// Accept transport properties in non-canonical form
|
||||
// TODO: Remove this after a reasonable migration period
|
||||
// (added 2023-02-17)
|
||||
super(clientHelper, metadataEncoder, clock, false);
|
||||
super(clientHelper, metadataEncoder, clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.briarproject.bramble.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxConstants;
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.UNKNOWN;
|
||||
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class QrCodeClassifierImpl implements QrCodeClassifier {
|
||||
|
||||
@Inject
|
||||
QrCodeClassifierImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<QrCodeType, Integer> classifyQrCode(String payload) {
|
||||
byte[] bytes = payload.getBytes(ISO_8859_1);
|
||||
if (bytes.length == 0) return new Pair<>(UNKNOWN, 0);
|
||||
// If this is a Bramble QR code then the first byte encodes the
|
||||
// format ID (3 bits) and version (5 bits)
|
||||
int formatIdAndVersion = bytes[0] & 0xFF;
|
||||
int formatId = formatIdAndVersion >> 5;
|
||||
int formatVersion = formatIdAndVersion & 0x1F;
|
||||
if (formatId == KeyAgreementConstants.QR_FORMAT_ID) {
|
||||
return new Pair<>(BQP, formatVersion);
|
||||
}
|
||||
if (formatId == MailboxConstants.QR_FORMAT_ID) {
|
||||
return new Pair<>(MAILBOX, formatVersion);
|
||||
}
|
||||
return new Pair<>(UNKNOWN, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.briarproject.bramble.qrcode;
|
||||
|
||||
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class QrCodeModule {
|
||||
|
||||
@Provides
|
||||
QrCodeClassifier provideQrCodeClassifier(
|
||||
QrCodeClassifierImpl qrCodeClassifier) {
|
||||
return qrCodeClassifier;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,6 @@ import static org.briarproject.bramble.api.sync.RecordTypes.OFFER;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.PRIORITY;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.REQUEST;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.VERSIONS;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
|
||||
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.PRIORITY_NONCE_BYTES;
|
||||
@@ -127,8 +126,6 @@ class SyncRecordReaderImpl implements SyncRecordReader {
|
||||
byte[] payload = nextRecord.getPayload();
|
||||
if (payload.length <= MESSAGE_HEADER_LENGTH)
|
||||
throw new FormatException();
|
||||
if (payload.length > MAX_MESSAGE_LENGTH)
|
||||
throw new FormatException();
|
||||
// Validate timestamp
|
||||
long timestamp = ByteUtils.readUint64(payload, UniqueId.LENGTH);
|
||||
if (timestamp < 0) throw new FormatException();
|
||||
|
||||
@@ -6,7 +6,6 @@ import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -22,15 +21,18 @@ public class DefaultTaskSchedulerModule {
|
||||
TaskScheduler scheduler;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
TaskScheduler provideTaskScheduler(LifecycleManager lifecycleManager,
|
||||
ThreadFactory threadFactory) {
|
||||
private final ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
public DefaultTaskSchedulerModule() {
|
||||
// Discard tasks that are submitted during shutdown
|
||||
RejectedExecutionHandler policy =
|
||||
new ScheduledThreadPoolExecutor.DiscardPolicy();
|
||||
ScheduledExecutorService scheduledExecutorService =
|
||||
new ScheduledThreadPoolExecutor(1, threadFactory, policy);
|
||||
scheduledExecutorService = new ScheduledThreadPoolExecutor(1, policy);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
TaskScheduler provideTaskScheduler(LifecycleManager lifecycleManager) {
|
||||
lifecycleManager.registerForShutdown(scheduledExecutorService);
|
||||
return new TaskSchedulerImpl(scheduledExecutorService);
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class DefaultThreadFactoryModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
ThreadFactory provideThreadFactory() {
|
||||
return Executors.defaultThreadFactory();
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,8 @@ class SessionParserImpl implements SessionParser {
|
||||
|
||||
@Override
|
||||
public Session parseSession(BdfDictionary meta) throws FormatException {
|
||||
State state = State.fromValue(meta.getInt(SESSION_KEY_STATE));
|
||||
State state =
|
||||
State.fromValue(meta.getLong(SESSION_KEY_STATE).intValue());
|
||||
|
||||
MessageId lastLocalMessageId = null;
|
||||
byte[] lastLocalMessageIdBytes =
|
||||
@@ -55,9 +56,9 @@ class SessionParserImpl implements SessionParser {
|
||||
Long localTimestamp = meta.getOptionalLong(SESSION_KEY_LOCAL_TIMESTAMP);
|
||||
|
||||
KeySetId keySetId = null;
|
||||
Integer keySetIdInt = meta.getOptionalInt(SESSION_KEY_KEY_SET_ID);
|
||||
if (keySetIdInt != null) {
|
||||
keySetId = new KeySetId(keySetIdInt);
|
||||
Long keySetIdLong = meta.getOptionalLong(SESSION_KEY_KEY_SET_ID);
|
||||
if (keySetIdLong != null) {
|
||||
keySetId = new KeySetId(keySetIdLong.intValue());
|
||||
}
|
||||
|
||||
return new Session(state, lastLocalMessageId, localKeyPair,
|
||||
|
||||
@@ -177,8 +177,8 @@ class TransportKeyAgreementManagerImpl extends BdfIncomingMessageHook
|
||||
protected DeliveryAction incomingMessage(Transaction txn, Message m,
|
||||
BdfList body, BdfDictionary meta)
|
||||
throws DbException, FormatException {
|
||||
MessageType type =
|
||||
MessageType.fromValue(meta.getInt(MSG_KEY_MESSAGE_TYPE));
|
||||
MessageType type = MessageType.fromValue(
|
||||
meta.getLong(MSG_KEY_MESSAGE_TYPE).intValue());
|
||||
TransportId t = new TransportId(meta.getString(MSG_KEY_TRANSPORT_ID));
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Received " + type + " message for " + t);
|
||||
|
||||
@@ -42,7 +42,7 @@ class TransportKeyAgreementValidator extends BdfMessageValidator {
|
||||
@Override
|
||||
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||
BdfList body) throws FormatException {
|
||||
MessageType type = MessageType.fromValue(body.getInt(0));
|
||||
MessageType type = MessageType.fromValue(body.getLong(0).intValue());
|
||||
if (type == KEY) return validateKeyMessage(m.getTimestamp(), body);
|
||||
else if (type == ACTIVATE) return validateActivateMessage(body);
|
||||
else throw new AssertionError();
|
||||
|
||||
@@ -228,11 +228,15 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
||||
Contact contact = db.getContact(txn, c);
|
||||
callVisibilityHooks(txn, contact, before, after);
|
||||
}
|
||||
// Broadcast events for any client version update
|
||||
// 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 (!oldRemoteStates.contains(cs)) {
|
||||
txn.attach(
|
||||
new ClientVersionUpdatedEvent(c, cs.clientVersion));
|
||||
if (!oldRemoteVersions.contains(cs.clientVersion)) {
|
||||
txn.attach(new ClientVersionUpdatedEvent(c,
|
||||
cs.clientVersion));
|
||||
}
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
@@ -301,8 +305,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
||||
for (int i = 0; i < size; i++) {
|
||||
BdfList cv = body.getList(i);
|
||||
ClientId clientId = new ClientId(cv.getString(0));
|
||||
int majorVersion = cv.getInt(1);
|
||||
int minorVersion = cv.getInt(2);
|
||||
int majorVersion = cv.getLong(1).intValue();
|
||||
int minorVersion = cv.getLong(2).intValue();
|
||||
parsed.add(new ClientVersion(clientId, majorVersion, minorVersion));
|
||||
}
|
||||
return parsed;
|
||||
@@ -408,8 +412,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
||||
throws FormatException {
|
||||
// Client ID, major version, minor version, active
|
||||
ClientId clientId = new ClientId(clientState.getString(0));
|
||||
int majorVersion = clientState.getInt(1);
|
||||
int minorVersion = clientState.getInt(2);
|
||||
int majorVersion = clientState.getLong(1).intValue();
|
||||
int minorVersion = clientState.getLong(2).intValue();
|
||||
boolean active = clientState.getBoolean(3);
|
||||
return new ClientState(clientId, majorVersion, minorVersion, active);
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ class ClientVersioningValidator extends BdfMessageValidator {
|
||||
checkSize(clientState, 4);
|
||||
String clientId = clientState.getString(0);
|
||||
checkLength(clientId, 1, MAX_CLIENT_ID_LENGTH);
|
||||
int majorVersion = clientState.getInt(1);
|
||||
int majorVersion = clientState.getLong(1).intValue();
|
||||
if (majorVersion < 0) throw new FormatException();
|
||||
int minorVersion = clientState.getInt(2);
|
||||
int minorVersion = clientState.getLong(2).intValue();
|
||||
if (minorVersion < 0) throw new FormatException();
|
||||
clientState.getBoolean(3);
|
||||
}
|
||||
|
||||
36
bramble-core/src/main/resources/bridges
Normal file
36
bramble-core/src/main/resources/bridges
Normal file
@@ -0,0 +1,36 @@
|
||||
d Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1
|
||||
d Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1
|
||||
d Bridge obfs4 38.229.33.83:80 0BAC39417268B96B9F514E7F63FA6FBA1A788955 cert=VwEFpk9F/UN9JED7XpG1XOjm/O8ZCXK80oPecgWnNDZDv5pdkhq1OpbAH0wNqOT6H6BmRQ iat-mode=1
|
||||
d Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0
|
||||
d Bridge obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0
|
||||
d Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
|
||||
d Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0
|
||||
d Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0
|
||||
d Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0
|
||||
d Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0
|
||||
d Bridge obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0
|
||||
d Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0
|
||||
d Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0
|
||||
n Bridge obfs4 185.181.11.86:443 A961609729E7FDF520B4E81F1F1B8FA1045285C3 cert=e5faG9Zk4Ni+e7z2YgGfevyKPQlMvkVGi4ublSsHYjaBovKeNXpOhbeFxzbZZoAzxAoGUQ iat-mode=0
|
||||
n Bridge obfs4 93.95.226.151:41185 460B0CFFC0CF1D965F3DE064E08BA1915E7C916A cert=inluPzp5Jp5OzZar1eQb4dcQ/YlAj/v0kHAUCoCr3rmLt03+pVuVTjoH4mRy4+acXpn+Gw iat-mode=0
|
||||
n Bridge obfs4 120.29.217.52:5223 40FE3DB9800272F9CDC76422F8ED7883280EE96D cert=/71PS4l8c/XJ4DIItlH9xMqNvPFg2RUTrHvPlQWh48u5et8h/yyyjCcYphUadDsfBWpaGQ iat-mode=0
|
||||
n Bridge obfs4 185.177.207.138:8443 53716FE26F23C8C6A71A2BC5D9D8DC64747278C7 cert=6jcYVilMEzxdsWghSrQFpYYJlkkir/GPIXw/EnddUK3S8ToVpMG8u1SwMOEdEs735RrMHw iat-mode=0
|
||||
n Bridge obfs4 87.121.72.109:9002 C8081D4731C953FA4AE166946E72B29153351E34 cert=bikAqxKV6Ch5gFCBTdPI28VeShYa1ZgkLmvc7YZNLWFsFZoaCULL/3AQKjpIfvSiJs5jGQ iat-mode=0
|
||||
n Bridge obfs4 70.34.249.113:443 F441B16ABB1055794C2CE01821FC05047B2C8CFC cert=MauLNoyq8EwjY4Qe0oASYzs2hXdSjNgy+BtP9oo1naHhRsyKTtAZzeNv08RnzWjMJrTwcg iat-mode=0
|
||||
n Bridge obfs4 104.168.68.90:443 ED55B3C321E44EA7E50EF568C8A63CF75E89A58C cert=fgonxDvltTp8nmcOE9sUG94eOAALxETVVXAwnTZJLPpf7rjPuTp+abKl4VyFkxfcLRr5KQ iat-mode=0
|
||||
n Bridge obfs4 158.247.207.151:443 6170ADBBB6C1859A8E7E4416BB8AB3AF471AE47F cert=Od4izlwLnXcq7LMSOJtnZLtklaUn+X+gPcBwN7RUEkk9rqxRRYNHW7as8g6+jheDsazxAQ iat-mode=0
|
||||
n Bridge obfs4 45.142.181.131:42069 6EBCF6B02DA2B982F4080A7119D737366AFB74FA cert=9HyWH/BCwWzNirZdTQtluCgJk+gFhpOqydIuyQ1iDvpnqsAynKF+zcPE/INZFefm86UlBg iat-mode=0
|
||||
n Bridge obfs4 85.214.28.204:47111 78A36E46BB082A471848239D3F4390A8F8C6084D cert=96sr3eaUFBDu4wFVAQIfNFElh0UNuutJ/3/Fh2Vu2PHfacQ8bwfY02bwG351U8BZpLnfUQ iat-mode=0
|
||||
n Bridge obfs4 152.67.77.101:4096 B82DB9CDDF887AB8A859420E07DF298E30AF8A6E cert=21OWn3yFo+hulmQNAOtF5uwwOqWtdT5PrLhk8BG9DpOd0/k5DEkQEYPyDdXbS9nZ0E5BJA iat-mode=0
|
||||
n Bridge obfs4 82.39.132.97:6969 F505EF4C41C77FFDC0C440C122A02129FBE25823 cert=bwNWuL7UYB9aiKajE1gkffylYx/EM4FjSZxIJ0pVT/xaR21xXlIdaXw7l+EYmC4nVIh2HQ iat-mode=0
|
||||
n Bridge obfs4 185.103.252.72:443 75F15E9339FF572F88F5588D429FEA379744BC53 cert=nOZ/SaRE3L1dChvjfe0Ks/wM/F8iFhwd3g2G5zgtcLB8x+wiZRWCwjRrbbiQyb3Gh2mxRQ iat-mode=0
|
||||
n Bridge obfs4 185.177.207.13:22662 928C1E4289A01F34C8FB423FC32C0E77EE0F8736 cert=p9L6+25s8bnfkye1ZxFeAE4mAGY7DH4Gaj7dxngIIzP9BtqrHHwZXdjMK0RVIQ34C7aqZw iat-mode=2
|
||||
n Bridge obfs4 207.181.229.55:40132 37FE8D782F5DD2BAEEDAAB8257B701344676B6DD cert=f5Hbfn3ToMzH170cV8DfLly3vRynveidfOfDcbradIDtbLDX15V2yQ8eEH2CPKQJmQR2Hg iat-mode=0
|
||||
n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert=+q0pjaiM0JMqHL/BKqCRD+pjflaw/S406eUDF7CnFgamvQW3l2HVLJhQ6uX9P8zff0PLGg iat-mode=0
|
||||
v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F
|
||||
v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31
|
||||
v Bridge 185.189.195.124:8199 A1F3EE78F9C2343668E68FEB84358A4C742831A5
|
||||
v Bridge 213.196.191.96:9060 05E222E2A8C234234FE0CEB58B08A93B8FC360DB
|
||||
v Bridge 75.100.92.154:22815 465E990FA8A752DDE861EDF31E42454ACC037AB4
|
||||
m Bridge meek_lite 192.0.2.2:80 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
|
||||
s Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72
|
||||
6
bramble-core/src/main/resources/snowflake-params
Normal file
6
bramble-core/src/main/resources/snowflake-params
Normal file
@@ -0,0 +1,6 @@
|
||||
ZZ F url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478
|
||||
ZZ A url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478
|
||||
ZZ M url=https://snowflake-broker.torproject.net/ ampcache=https://cdn.ampproject.org/ front=cdn.ampproject.org ice=stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478
|
||||
TM F url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479
|
||||
TM A url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479
|
||||
TM M url=https://snowflake-broker.torproject.net/ ampcache=https://cdn.ampproject.org/ front=cdn.ampproject.org ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479
|
||||
@@ -20,6 +20,7 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -33,7 +34,6 @@ import static org.briarproject.bramble.test.TestUtils.getIdentity;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
@@ -342,7 +342,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
private void storeDatabaseKey(File f, String hex) throws IOException {
|
||||
f.getParentFile().mkdirs();
|
||||
FileOutputStream out = new FileOutputStream(f);
|
||||
out.write(hex.getBytes(UTF_8));
|
||||
out.write(hex.getBytes(Charset.forName("UTF-8")));
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
@@ -350,7 +350,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||
@Nullable
|
||||
private String loadDatabaseKey(File f) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(f), UTF_8));
|
||||
new FileInputStream(f), Charset.forName("UTF-8")));
|
||||
String hex = reader.readLine();
|
||||
reader.close();
|
||||
return hex;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp - MAX_CLOCK_DIFFERENCE));
|
||||
oneOf(clientHelper).toList(message, true);
|
||||
oneOf(clientHelper).toList(message.getBody());
|
||||
will(returnValue(body));
|
||||
oneOf(metadataEncoder).encode(dictionary);
|
||||
will(returnValue(meta));
|
||||
@@ -86,7 +86,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(clientHelper).toList(shortMessage, true);
|
||||
oneOf(clientHelper).toList(shortMessage.getBody());
|
||||
will(returnValue(body));
|
||||
oneOf(metadataEncoder).encode(dictionary);
|
||||
will(returnValue(meta));
|
||||
@@ -114,7 +114,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(clientHelper).toList(message, true);
|
||||
oneOf(clientHelper).toList(message.getBody());
|
||||
will(throwException(new FormatException()));
|
||||
}});
|
||||
|
||||
@@ -126,7 +126,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(clientHelper).toList(message, true);
|
||||
oneOf(clientHelper).toList(message.getBody());
|
||||
will(returnValue(body));
|
||||
}});
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user