Compare commits

...

97 Commits

Author SHA1 Message Date
akwizgran
eb98cb2f23 Pause between connection attempts. 2018-05-31 09:59:06 +01:00
akwizgran
aa331981cc Don't poll again if last poll is still running. 2018-05-31 09:59:05 +01:00
akwizgran
e9a6faad2d Poll contacts in series rather than parallel. 2018-05-31 09:59:01 +01:00
akwizgran
5025cf1e40 Merge branch 'remove-removable-drive-plugin' into 'master'
Remove RemovableDrivePlugin, refactor plugin interface

Closes #25

See merge request akwizgran/briar!817
2018-05-31 08:49:32 +00:00
akwizgran
834342fd3a Merge branch 'remove-reblog-scene-transition' into 'master'
Disable reblog scene transition as it even crashes my Android 7.1 device

Closes #785

See merge request akwizgran/briar!821
2018-05-29 15:56:07 +00:00
akwizgran
3028b236e1 Merge branch 'disable-prefetching' into 'master'
Disable pre-fetching in Threaded RecyclerView as a workaround for #1289

See merge request akwizgran/briar!820
2018-05-29 15:47:15 +00:00
Torsten Grote
254422bc02 Disable reblog scene transition as it even crashes my Android 7.1 device
Closes #785
2018-05-29 12:44:41 -03:00
Torsten Grote
c7949d6e00 Disable pre-fetching in Threaded RecyclerView as a workaround for #1289 2018-05-29 12:29:40 -03:00
Torsten Grote
0187264da7 Merge branch '1219-remove-debug-logging' into 'master'
Remove debug logging from setup process

See merge request akwizgran/briar!819
2018-05-28 13:59:22 +00:00
akwizgran
85a18cf53f Remove debug logging from setup process. 2018-05-28 14:34:20 +01:00
akwizgran
3181b695df Remove RemovableDrivePlugin, refactor plugin interface. 2018-05-25 13:57:38 +01:00
akwizgran
b2ac210586 Merge branch 'factor_out_plugin_conf' into 'master'
Make plugins and polling configurable

See merge request akwizgran/briar!814
2018-05-24 16:34:05 +00:00
Torsten Grote
d20340416d Merge branch 'jcenter-tor-binaries' into 'master'
Download Tor binaries from JCenter

See merge request akwizgran/briar!816
2018-05-24 12:21:14 +00:00
akwizgran
9da871718c Download Tor binaries from JCenter. 2018-05-24 10:54:34 +01:00
goapunk
3793cb841b Fix test and poller instantiation 2018-05-23 14:39:01 +02:00
goapunk
c6b88b51f0 Make plugins and polling configurable
* Move PluginConfig out of bramble-android. Projects using bramble now need to provide it.
* Add a PluginConfig#shouldPoll() method which can be used to disable polling altogether.
* Move Poller instantiation to the PluginManager.
2018-05-23 14:39:00 +02:00
Torsten Grote
2f00215a44 Merge branch 'remove-jtorctl-jar' into 'master'
Replace jtorctl jar with JCenter dependency

See merge request akwizgran/briar!815
2018-05-23 11:22:15 +00:00
akwizgran
183f0c5f31 Bump version numbers for 1.0.5 release. 2018-05-22 15:30:33 +01:00
Torsten Grote
34c5aaae0a Update translations (Farsi and Chinese complete now) 2018-05-22 11:17:51 -03:00
Torsten Grote
5531355ebd Merge branch '1219-store-db-key-in-file' into 'master'
Store database key in a file

Closes #1219

See merge request akwizgran/briar!810
2018-05-22 12:24:13 +00:00
akwizgran
b9e607744a Store second copy of DB key in backup file. 2018-05-22 12:07:07 +01:00
akwizgran
def62bce5a Replace jtorctl jar with JCenter dependency. 2018-05-22 11:32:19 +01:00
akwizgran
9dae3d191a Merge branch '1281-introduction-bug' into 'master'
Introduction: Reset session information for removed introducees

Closes #1281

See merge request akwizgran/briar!813
2018-05-22 09:09:13 +00:00
Torsten Grote
20422edf78 Introduction: Reset session information for removed introducees 2018-05-21 16:26:11 -03:00
Torsten Grote
f8bc5f08bf Merge branch 'unicode-escapes-for-test-data' into 'master'
Escape Unicode characters in test data

See merge request akwizgran/briar!812
2018-05-21 16:21:29 +00:00
akwizgran
9434495d70 Escape Unicode characters in test data.
This enables reproducible builds with non-Unicode locales.
2018-05-21 17:02:09 +01:00
Torsten Grote
bf9e91fcf5 Merge branch 'fix-build-timestamp' into 'master'
Make build timestamp command compatible with old versions of Git

See merge request akwizgran/briar!811
2018-05-21 12:47:47 +00:00
akwizgran
d9d86206a6 Make build timestamp command compatible with old versions of Git. 2018-05-21 13:40:14 +01:00
akwizgran
b410b8efcc Don't overwrite the backup if it's our only copy. 2018-05-18 15:17:43 +01:00
akwizgran
39aa2d96b3 Unit tests for DB key storage and retrieval. 2018-05-18 15:11:28 +01:00
akwizgran
21dae824a6 Store database key in a file rather than shared prefs. 2018-05-18 14:47:53 +01:00
akwizgran
cfdbd29cb4 Remove unused logging methods. 2018-05-18 14:47:20 +01:00
akwizgran
4df335ebd3 Merge branch 'own-ci' into 'master'
Switch to our own CI image

See merge request akwizgran/briar!809
2018-05-18 10:01:04 +00:00
Torsten Grote
682bee1486 Switch to our own CI image 2018-05-17 18:52:49 -03:00
akwizgran
f31219d54b Bump version numbers for 1.0.4 release. 2018-05-17 16:22:39 +01:00
Torsten Grote
b0ea32c85f Merge branch 'hide-testing-preferences' into 'master'
Hide testing preferences in release builds

See merge request akwizgran/briar!808
2018-05-17 14:33:06 +00:00
akwizgran
651e0b9859 Hide testing preferences in release builds. 2018-05-17 15:14:59 +01:00
akwizgran
f66244b578 Merge branch 'ci-test' into 'master'
Add CI stage for testing reproducibility

See merge request akwizgran/briar!807
2018-05-17 14:13:40 +00:00
Torsten Grote
3a35effae9 Add CI stage for testing reproducibility 2018-05-17 10:55:56 -03:00
Torsten Grote
97f4cd039a Update translations. New: Farsi and Asturian 2018-05-17 10:55:08 -03:00
akwizgran
20a1474457 Merge branch '1277-bdf-reader' into 'master'
Use lower default limit for BDF strings and raws

Closes #1277

See merge request akwizgran/briar!805
2018-05-17 13:35:57 +00:00
Torsten Grote
f214208b0a Merge branch 'test-data-lan-addresses' into 'master'
Use more realistic LAN addresses for test contacts

See merge request akwizgran/briar!804
2018-05-17 11:53:48 +00:00
akwizgran
d95a5fd58c Use lower default limit for BDF strings and raws. 2018-05-17 12:33:56 +01:00
akwizgran
7d4de21be0 Merge branch 'decouple-report-sender' into 'master'
Decouple DevReportSender from TorPlugin

See merge request akwizgran/briar!803
2018-05-17 09:52:47 +00:00
akwizgran
a65bda04bf Use more realistic LAN addresses for test contacts. 2018-05-17 10:22:29 +01:00
akwizgran
41ae7b0522 Merge branch 'internal-logger' into 'master'
Use internal log handler for crash reports and feedback

Closes #986

See merge request akwizgran/briar!802
2018-05-17 08:22:48 +00:00
akwizgran
c2214f5e61 Merge branch 'message-encrypter' into 'master'
Refactor MessageEncrypter main method

See merge request akwizgran/briar!800
2018-05-17 08:22:22 +00:00
akwizgran
75bd7927ac Decouple DevReportSender from TorPlugin. 2018-05-17 09:20:12 +01:00
akwizgran
ed2c0336ed Merge branch 'crash-button' into 'master'
Add crash button to UI in debug mode

See merge request akwizgran/briar!801
2018-05-16 11:19:15 +00:00
akwizgran
b23baf70b4 Don't send system logs in ACRA reports. 2018-05-16 11:53:00 +01:00
akwizgran
17a7144194 Add internal logger. 2018-05-16 11:52:55 +01:00
akwizgran
0f7d27cd95 Refactor MessageEncrypter main method. 2018-05-16 11:42:00 +01:00
akwizgran
6735e5075b Add crash button to UI in debug mode. 2018-05-16 11:40:50 +01:00
akwizgran
135cf086f6 Merge branch 'silence_strict_mode' into 'master'
Silence known strict mode violations

See merge request akwizgran/briar!795
2018-05-16 09:48:54 +00:00
goapunk
72bac59989 silence known strict mode violations 2018-05-15 17:29:53 +02:00
Torsten Grote
2c99a75b4e Merge branch 'settings-screen' into 'master'
Use lighter text colour for preference categories and fix layout

See merge request akwizgran/briar!799
2018-05-15 14:20:01 +00:00
Torsten Grote
5c068ed07b Merge branch 'remove-unbound-transport-keys' into 'master'
Remove support for unbound transport keys

See merge request akwizgran/briar!791
2018-05-15 14:18:58 +00:00
akwizgran
fec384c200 Add migration for NOT NULL constraint. 2018-05-15 14:37:36 +01:00
akwizgran
89a4d1922b Remove support for unbound transport keys. 2018-05-15 14:37:36 +01:00
akwizgran
6ed16802ce Merge branch 'deterministic-build-timestamp' into 'master'
Use git commit timestamp as build timestamp

See merge request akwizgran/briar!798
2018-05-15 13:24:00 +00:00
akwizgran
7f11d7280f Merge branch 'animal-sniffer' into 'master'
Use Animal Sniffer to check Java 6 API compatibility

See merge request akwizgran/briar!787
2018-05-15 13:23:29 +00:00
akwizgran
597e2a233f Use git commit timestamp as build timestamp. 2018-05-15 12:47:14 +01:00
akwizgran
e83d8bb700 Bump version numbers for 1.0.3 release. 2018-05-14 21:52:16 +01:00
akwizgran
d1ce0d0628 Merge branch '1215-low-memory-hide-ui' into 'master'
Clear the UI when memory is critically low

See merge request akwizgran/briar!786
2018-05-14 14:53:04 +00:00
akwizgran
d73ec3cd88 Merge branch 'disable-expiry' into 'master'
Disable expiry for release builds

See merge request akwizgran/briar!797
2018-05-14 14:51:40 +00:00
akwizgran
71c66c843b Merge branch '1219-commit-shared-prefs' into 'master'
Commit shared preferences, clear instead of deleting

See merge request akwizgran/briar!794
2018-05-14 14:18:49 +00:00
akwizgran
bd19272099 Throw exception if account exists when beginning setup. 2018-05-14 14:20:13 +01:00
akwizgran
b77b885a94 Commit shared preferences, clear instead of deleting. 2018-05-14 14:20:12 +01:00
akwizgran
1fc4f657c7 Merge branch '1219-account-exists' into 'master'
Add logging to debug account creation and deletion

See merge request akwizgran/briar!793
2018-05-14 13:19:20 +00:00
akwizgran
df7d48d54d Fix test expectations. 2018-05-14 12:35:03 +01:00
akwizgran
1987dcb936 Make field that's used on background thread volatile. 2018-05-14 12:34:32 +01:00
akwizgran
f3b69a26f8 Remove unused exception declarations. 2018-05-14 12:31:48 +01:00
akwizgran
5e0ca10dae Add logging to debug account setup. 2018-05-14 12:31:46 +01:00
akwizgran
685496fb15 Extract DatabaseConfig implementation. 2018-05-14 12:30:57 +01:00
akwizgran
1521cdd258 Move expiry date to TestingConstants. 2018-05-14 12:24:37 +01:00
akwizgran
80561910b1 Disable expiry for release builds. 2018-05-14 12:03:30 +01:00
akwizgran
bffb5c94ed Merge branch '1229-setup-crash' into 'master'
Store nickname and password across screen rotations

Closes #1229

See merge request akwizgran/briar!796
2018-05-14 09:57:12 +00:00
akwizgran
dcbb3e76d4 Use lighter text colour for preference categories. 2018-05-11 12:48:43 +01:00
akwizgran
c19f7c27b1 Merge branch 'stream-writer-interface' into 'master'
Send end of stream marker when sync session finishes

See merge request akwizgran/briar!790
2018-05-11 10:55:32 +00:00
akwizgran
9a5a1489ef Remove a redundant method. 2018-05-11 11:41:49 +01:00
akwizgran
648793e092 Add javadoc. 2018-05-11 11:36:49 +01:00
akwizgran
e10742a23d Store nickname and password across screen rotations. 2018-05-11 11:36:04 +01:00
akwizgran
32ada51831 Log transport ID with number of connected contacts. 2018-05-10 12:31:54 +01:00
akwizgran
7734a62c3e Interrupt outgoing session when incoming session ends. 2018-05-10 12:29:45 +01:00
akwizgran
e516c329a1 Bump version numbers for 1.0.2 release. 2018-05-09 16:59:09 +01:00
Torsten Grote
b839041d5a Update translations 2018-05-09 09:56:15 -03:00
Torsten Grote
65de8707b7 Merge branch '1225-improve-setup-ux' into 'master'
Remove circle, make button flat to improve setup UX

Closes #1225

See merge request akwizgran/briar!792
2018-05-09 10:41:17 +00:00
akwizgran
dc5bd39ce4 Remove circle, make button flat to improve setup UX. 2018-05-09 10:50:23 +01:00
akwizgran
3c4513b9c7 Convert test to BrambleMockTestCase. 2018-05-08 15:02:07 +01:00
akwizgran
5320737d49 Send end of stream marker when sync session finishes. 2018-05-08 14:41:53 +01:00
akwizgran
0ad9415850 Merge branch 'fix-javadoc' into 'master'
Fix random javadoc errors

See merge request akwizgran/briar!789
2018-05-08 12:56:25 +00:00
goapunk
6f1fba44b6 Fix random javadoc errors 2018-05-08 14:42:14 +02:00
akwizgran
286937e472 Resolve merge conflicts. 2018-05-07 14:37:32 +00:00
akwizgran
ed53544226 Clear the UI in onLowMemory() if SDK_INT < 16. 2018-05-04 12:18:52 +01:00
akwizgran
6da45a4585 Clear the UI when memory is critically low. 2018-05-04 12:04:13 +01:00
akwizgran
a3b5ff0bc0 Add dependency hashes for Animal Sniffer. 2018-05-02 16:24:59 +01:00
akwizgran
1192f66487 Use Animal Sniffer to check Java 6 API compatibility. 2018-05-02 16:10:40 +01:00
200 changed files with 3027 additions and 3111 deletions

View File

@@ -1,27 +1,31 @@
image: registry.gitlab.com/fdroid/ci-images-base:latest image: briar/ci-image-android:latest
cache:
paths:
- .gradle/wrapper
- .gradle/caches
before_script:
- set -e
- export GRADLE_USER_HOME=$PWD/.gradle
# Download OpenJDK 6 so we can compile against its standard library
- JDK_FILE=openjdk-6-jre-headless_6b38-1.13.10-1~deb7u1_amd64.deb
- if [ ! -d openjdk ]
- then
- wget -q http://ftp.uk.debian.org/debian/pool/main/o/openjdk-6/$JDK_FILE
- dpkg-deb -x $JDK_FILE openjdk
- fi
- export JAVA_6_HOME=$PWD/openjdk/usr/lib/jvm/java-6-openjdk-amd64
test: test:
script: before_script:
- ./gradlew test - set -e
- export GRADLE_USER_HOME=$PWD/.gradle
after_script: cache:
# this file changes every time but should not be cached paths:
- .gradle/wrapper
- .gradle/caches
script:
- ./gradlew --no-daemon animalSnifferMain animalSnifferTest
- ./gradlew --no-daemon test
after_script:
# these file change every time but should not be cached
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock - rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/ - rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
test_reproducible:
image: briar/reproducer:latest
script:
- cd /opt/briar-reproducer
- ./reproduce.py ${CI_COMMIT_REF_NAME}
only:
- tags

View File

@@ -1,11 +1,5 @@
import de.undercouch.gradle.tasks.download.Download
import de.undercouch.gradle.tasks.download.Verify
import java.security.NoSuchAlgorithmException
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'witness' apply plugin: 'witness'
apply plugin: 'de.undercouch.download'
android { android {
compileSdkVersion 27 compileSdkVersion 27
@@ -14,8 +8,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 26 targetSdkVersion 26
versionCode 10001 versionCode 10005
versionName "1.0.1" versionName "1.0.5"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
} }
@@ -25,9 +19,14 @@ android {
} }
} }
configurations {
tor
}
dependencies { dependencies {
implementation project(path: ':bramble-core', configuration: 'default') implementation project(path: ':bramble-core', configuration: 'default')
implementation fileTree(dir: 'libs', include: '*.jar') implementation 'org.briarproject:jtorctl:0.3'
tor 'org.briarproject:tor-android:0.2.9.15@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2' annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
@@ -92,6 +91,8 @@ dependencyVerification {
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa', 'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca', 'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349', 'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
'org.briarproject:jtorctl:0.3:jtorctl-0.3.jar:f2939238a097898998432effe93b0334d97a787972ab3a91a8973a1d309fc864',
'org.briarproject:tor-android:0.2.9.15:tor-android-0.2.9.15.zip:34a6474ee219ffa52e0f3393e917dda6ed03d320b02247d4fa5075aa4094ee6d',
'org.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4', 'org.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d', 'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa', 'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
@@ -112,81 +113,9 @@ dependencyVerification {
] ]
} }
ext.torBinaryDir = 'src/main/res/raw'
ext.torVersion = '0.2.9.14'
ext.geoipVersion = '2017-11-06'
ext.torDownloadUrl = 'https://briarproject.org/build/'
def torBinaries = [
"tor_arm" : '1710ea6c47b7f4c1a88bdf4858c7893837635db10e8866854eed8d61629f50e8',
"tor_arm_pie": '974e6949507db8fa2ea45231817c2c3677ed4ccf5488a2252317d744b0be1917',
"tor_x86" : '3a5e45b3f051fcda9353b098b7086e762ffe7ba9242f7d7c8bf6523faaa8b1e9',
"tor_x86_pie": 'd1d96d8ce1a4b68accf04850185780d10cd5563d3552f7e1f040f8ca32cb4e51',
"geoip" : '8239b98374493529a29096e45fc5877d4d6fdad0146ad8380b291f90d61484ea'
]
def verifyOrDeleteBinary(name, chksum, alreadyVerified) {
return tasks.create("verifyOrDeleteBinary${name}", VerifyOrDelete) {
src "${torBinaryDir}/${name}.zip"
algorithm 'SHA-256'
checksum chksum
result alreadyVerified
onlyIf {
src.exists()
}
}
}
def downloadBinary(name, chksum, alreadyVerified) {
return tasks.create([
name: "downloadBinary${name}",
type: Download,
dependsOn: verifyOrDeleteBinary(name, chksum, alreadyVerified)]) {
src "${torDownloadUrl}${name}.zip"
.replace('tor_', "tor-${torVersion}-")
.replace('geoip', "geoip-${geoipVersion}")
.replaceAll('_', '-')
dest "${torBinaryDir}/${name}.zip"
onlyIf {
!dest.exists()
}
}
}
def verifyBinary(name, chksum) {
boolean[] alreadyVerified = [false]
return tasks.create([
name : "verifyBinary${name}",
type : Verify,
dependsOn: downloadBinary(name, chksum, alreadyVerified)]) {
src "${torBinaryDir}/${name}.zip"
algorithm 'SHA-256'
checksum chksum
onlyIf {
!alreadyVerified[0]
}
}
}
project.afterEvaluate { project.afterEvaluate {
torBinaries.every { name, checksum -> copy {
preBuild.dependsOn.add(verifyBinary(name, checksum)) from configurations.tor.collect { zipTree(it) }
} into 'src/main/res/raw'
}
class VerifyOrDelete extends Verify {
boolean[] result
@TaskAction
@Override
void verify() throws IOException, NoSuchAlgorithmException {
try {
super.verify()
result[0] = true
} catch (Exception e) {
println "${src} failed verification - deleting"
src.delete()
}
} }
} }

View File

@@ -1,12 +1,10 @@
package org.briarproject.bramble; package org.briarproject.bramble;
import org.briarproject.bramble.plugin.AndroidPluginModule;
import org.briarproject.bramble.system.AndroidSystemModule; import org.briarproject.bramble.system.AndroidSystemModule;
import dagger.Module; import dagger.Module;
@Module(includes = { @Module(includes = {
AndroidPluginModule.class,
AndroidSystemModule.class AndroidSystemModule.class
}) })
public class BrambleAndroidModule { public class BrambleAndroidModule {

View File

@@ -1,69 +0,0 @@
package org.briarproject.bramble.plugin;
import android.app.Application;
import android.content.Context;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
import org.briarproject.bramble.plugin.tor.TorPluginFactory;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.SocketFactory;
import dagger.Module;
import dagger.Provides;
@Module
public class AndroidPluginModule {
@Provides
PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
AndroidExecutor androidExecutor, SecureRandom random,
SocketFactory torSocketFactory, BackoffFactory backoffFactory,
Application app, LocationUtils locationUtils, DevReporter reporter,
EventBus eventBus) {
Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth =
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
appContext, random, eventBus, backoffFactory);
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, scheduler,
appContext, locationUtils, reporter, eventBus,
torSocketFactory, backoffFactory);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
scheduler, backoffFactory, appContext);
Collection<DuplexPluginFactory> duplex =
Arrays.asList(bluetooth, tor, lan);
@NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() {
@Override
public Collection<DuplexPluginFactory> getDuplexFactories() {
return duplex;
}
@Override
public Collection<SimplexPluginFactory> getSimplexFactories() {
return Collections.emptyList();
}
};
return pluginConfig;
}
}

View File

@@ -32,11 +32,9 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent; import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
@@ -52,7 +50,6 @@ import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -114,7 +111,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
private final Context appContext; private final Context appContext;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
private final DevReporter reporter;
private final SocketFactory torSocketFactory; private final SocketFactory torSocketFactory;
private final Backoff backoff; private final Backoff backoff;
private final DuplexPluginCallback callback; private final DuplexPluginCallback callback;
@@ -136,14 +132,13 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
TorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler, TorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
Context appContext, LocationUtils locationUtils, Context appContext, LocationUtils locationUtils,
DevReporter reporter, SocketFactory torSocketFactory, SocketFactory torSocketFactory, Backoff backoff,
Backoff backoff, DuplexPluginCallback callback, DuplexPluginCallback callback, String architecture,
String architecture, int maxLatency, int maxIdleTime) { int maxLatency, int maxIdleTime) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler; this.scheduler = scheduler;
this.appContext = appContext; this.appContext = appContext;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
this.reporter = reporter;
this.torSocketFactory = torSocketFactory; this.torSocketFactory = torSocketFactory;
this.backoff = backoff; this.backoff = backoff;
this.callback = callback; this.callback = callback;
@@ -338,7 +333,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return zin; return zin;
} }
private InputStream getConfigInputStream() throws IOException { private InputStream getConfigInputStream() {
int resId = getResourceId("torrc"); int resId = getResourceId("torrc");
return appContext.getResources().openRawResource(resId); return appContext.getResources().openRawResource(resId);
} }
@@ -389,14 +384,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
} }
private void sendDevReports() {
ioExecutor.execute(() -> {
// TODO: Trigger this with a TransportEnabledEvent
File reportDir = AndroidUtils.getReportDir(appContext);
reporter.sendReports(reportDir);
});
}
private void bind() { private void bind() {
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
// If there's already a port number stored in config, reuse it // If there's already a port number stored in config, reuse it
@@ -511,7 +498,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public void stop() throws PluginException { public void stop() {
running = false; running = false;
tryToClose(socket); tryToClose(socket);
if (networkStateReceiver != null) if (networkStateReceiver != null)
@@ -545,20 +532,16 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public void poll(Collection<ContactId> connected) { public void poll(Map<ContactId, TransportProperties> contacts) {
if (!isRunning()) return; if (!isRunning()) return;
backoff.increment(); backoff.increment();
Map<ContactId, TransportProperties> remote = for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
callback.getRemoteProperties(); connectAndCallBack(e.getKey(), e.getValue());
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
} }
} }
private void connectAndCallBack(ContactId c, TransportProperties p) { private void connectAndCallBack(ContactId c, TransportProperties p) {
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
@@ -568,13 +551,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (!isRunning()) return null; if (!isRunning()) return null;
return createConnection(callback.getRemoteProperties(c));
}
@Nullable
private DuplexTransportConnection createConnection(TransportProperties p) {
String onion = p.get(PROP_ONION); String onion = p.get(PROP_ONION);
if (StringUtils.isNullOrEmpty(onion)) return null; if (StringUtils.isNullOrEmpty(onion)) return null;
if (!ONION.matcher(onion).matches()) { if (!ONION.matcher(onion).matches()) {
@@ -624,10 +602,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
connectionStatus.getAndSetCircuitBuilt()) { connectionStatus.getAndSetCircuitBuilt()) {
LOG.info("First circuit built"); LOG.info("First circuit built");
backoff.reset(); backoff.reset();
if (isRunning()) { if (isRunning()) callback.transportEnabled();
sendDevReports();
callback.transportEnabled();
}
} }
} }
@@ -656,10 +631,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) {
connectionStatus.setBootstrapped(); connectionStatus.setBootstrapped();
backoff.reset(); backoff.reset();
if (isRunning()) { if (isRunning()) callback.transportEnabled();
sendDevReports();
callback.transportEnabled();
}
} }
} }

View File

@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.AndroidUtils;
@@ -40,21 +39,18 @@ public class TorPluginFactory implements DuplexPluginFactory {
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
private final Context appContext; private final Context appContext;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
private final DevReporter reporter;
private final EventBus eventBus; private final EventBus eventBus;
private final SocketFactory torSocketFactory; private final SocketFactory torSocketFactory;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
public TorPluginFactory(Executor ioExecutor, public TorPluginFactory(Executor ioExecutor,
ScheduledExecutorService scheduler, Context appContext, ScheduledExecutorService scheduler, Context appContext,
LocationUtils locationUtils, DevReporter reporter, LocationUtils locationUtils, EventBus eventBus,
EventBus eventBus, SocketFactory torSocketFactory, SocketFactory torSocketFactory, BackoffFactory backoffFactory) {
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler; this.scheduler = scheduler;
this.appContext = appContext; this.appContext = appContext;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
this.reporter = reporter;
this.eventBus = eventBus; this.eventBus = eventBus;
this.torSocketFactory = torSocketFactory; this.torSocketFactory = torSocketFactory;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
@@ -94,7 +90,7 @@ public class TorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorPlugin plugin = new TorPlugin(ioExecutor, scheduler, appContext, TorPlugin plugin = new TorPlugin(ioExecutor, scheduler, appContext,
locationUtils, reporter, torSocketFactory, backoff, callback, locationUtils, torSocketFactory, backoff, callback,
architecture, MAX_LATENCY, MAX_IDLE_TIME); architecture, MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;

View File

@@ -9,6 +9,7 @@ import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.os.Build; import android.os.Build;
import android.os.Parcel; import android.os.Parcel;
import android.os.StrictMode;
import android.provider.Settings; import android.provider.Settings;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -66,9 +67,12 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
@Override @Override
protected void writeSeed() { protected void writeSeed() {
// Silence strict mode
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites();
super.writeSeed(); super.writeSeed();
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18) if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18)
applyOpenSslFix(); applyOpenSslFix();
StrictMode.setThreadPolicy(tp);
} }
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html // Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html

View File

@@ -1,7 +1,9 @@
package org.briarproject.bramble.util; package org.briarproject.bramble.util;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build; import android.os.Build;
import android.provider.Settings; import android.provider.Settings;
@@ -10,11 +12,15 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Logger;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
public class AndroidUtils { public class AndroidUtils {
private static final Logger LOG =
Logger.getLogger(AndroidUtils.class.getName());
// Fake Bluetooth address returned by BluetoothAdapter on API 23 and later // Fake Bluetooth address returned by BluetoothAdapter on API 23 and later
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00"; private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
@@ -35,6 +41,7 @@ public class AndroidUtils {
public static String getBluetoothAddress(Context ctx, public static String getBluetoothAddress(Context ctx,
BluetoothAdapter adapter) { BluetoothAdapter adapter) {
// Return the adapter's address if it's valid and not fake // Return the adapter's address if it's valid and not fake
@SuppressLint("HardwareIds")
String address = adapter.getAddress(); String address = adapter.getAddress();
if (isValidBluetoothAddress(address)) return address; if (isValidBluetoothAddress(address)) return address;
// Return the address from settings if it's valid and not fake // Return the address from settings if it's valid and not fake
@@ -51,17 +58,28 @@ public class AndroidUtils {
&& !address.equals(FAKE_BLUETOOTH_ADDRESS); && !address.equals(FAKE_BLUETOOTH_ADDRESS);
} }
public static void deleteAppData(Context ctx) { public static void deleteAppData(Context ctx, SharedPreferences... clear) {
// Clear and commit shared preferences
for (SharedPreferences prefs : clear) {
if (!prefs.edit().clear().commit())
LOG.warning("Could not clear shared preferences");
}
// Delete files, except lib and shared_prefs directories
File dataDir = new File(ctx.getApplicationInfo().dataDir); File dataDir = new File(ctx.getApplicationInfo().dataDir);
File[] children = dataDir.listFiles(); File[] children = dataDir.listFiles();
if (children != null) { if (children == null) {
LOG.warning("Could not list files in app data dir");
} else {
for (File child : children) { for (File child : children) {
if (!child.getName().equals("lib")) String name = child.getName();
if (!name.equals("lib") && !name.equals("shared_prefs")) {
IoUtils.deleteFileOrDir(child); IoUtils.deleteFileOrDir(child);
}
} }
} }
// Recreate the cache dir as some OpenGL drivers expect it to exist // Recreate the cache dir as some OpenGL drivers expect it to exist
new File(dataDir, "cache").mkdir(); if (!new File(dataDir, "cache").mkdir())
LOG.warning("Could not recreate cache dir");
} }
public static File getReportDir(Context ctx) { public static File getReportDir(Context ctx) {

View File

@@ -2,6 +2,7 @@ apply plugin: 'java-library'
sourceCompatibility = 1.8 sourceCompatibility = 1.8
targetCompatibility = 1.8 targetCompatibility = 1.8
apply plugin: 'ru.vyarus.animalsniffer'
apply plugin: 'witness' apply plugin: 'witness'
dependencies { dependencies {
@@ -14,6 +15,8 @@ dependencies {
testImplementation "org.jmock:jmock-legacy:2.8.2" testImplementation "org.jmock:jmock-legacy:2.8.2"
testImplementation "org.hamcrest:hamcrest-library:1.3" testImplementation "org.hamcrest:hamcrest-library:1.3"
testImplementation "org.hamcrest:hamcrest-core:1.3" testImplementation "org.hamcrest:hamcrest-core:1.3"
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
} }
dependencyVerification { dependencyVerification {
@@ -26,6 +29,9 @@ dependencyVerification {
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8', 'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8', 'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
@@ -33,6 +39,7 @@ dependencyVerification {
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760', 'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16', 'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80', 'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220', 'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
] ]
} }
@@ -48,8 +55,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
artifacts { artifacts {
testOutput jarTest testOutput jarTest
} }
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
tasks.withType(JavaCompile) {
useJava6StandardLibrary(it)
}

View File

@@ -45,9 +45,9 @@ public interface ContactManager {
* *
* @param alice true if the local party is Alice * @param alice true if the local party is Alice
*/ */
ContactId addContact(Author remote, AuthorId local, ContactId addContact(Author remote, AuthorId local, SecretKey master,
SecretKey master, long timestamp, boolean alice, boolean verified, long timestamp, boolean alice, boolean verified, boolean active)
boolean active) throws DbException; throws DbException;
/** /**
* Returns the contact with the given ID. * Returns the contact with the given ID.

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* A key pair consisting of a {@link PublicKey} and a {@link PrivateKey). * A key pair consisting of a {@link PublicKey} and a {@link PrivateKey}.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault

View File

@@ -8,6 +8,7 @@ import java.io.IOException;
public interface BdfReader { public interface BdfReader {
int DEFAULT_NESTED_LIMIT = 5; int DEFAULT_NESTED_LIMIT = 5;
int DEFAULT_MAX_BUFFER_SIZE = 64 * 1024;
boolean eof() throws IOException; boolean eof() throws IOException;
@@ -39,13 +40,13 @@ public interface BdfReader {
boolean hasString() throws IOException; boolean hasString() throws IOException;
String readString(int maxLength) throws IOException; String readString() throws IOException;
void skipString() throws IOException; void skipString() throws IOException;
boolean hasRaw() throws IOException; boolean hasRaw() throws IOException;
byte[] readRaw(int maxLength) throws IOException; byte[] readRaw() throws IOException;
void skipRaw() throws IOException; void skipRaw() throws IOException;

View File

@@ -9,5 +9,6 @@ public interface BdfReaderFactory {
BdfReader createReader(InputStream in); BdfReader createReader(InputStream in);
BdfReader createReader(InputStream in, int nestedLimit); BdfReader createReader(InputStream in, int nestedLimit,
int maxBufferSize);
} }

View File

@@ -104,18 +104,12 @@ public interface DatabaseComponent {
throws DbException; throws DbException;
/** /**
* Stores the given transport keys, optionally binding them to the given * Stores the given transport keys for the given contact and returns a
* contact, and returns a key set ID. * key set ID.
*/ */
KeySetId addTransportKeys(Transaction txn, @Nullable ContactId c, KeySetId addTransportKeys(Transaction txn, ContactId c,
TransportKeys k) throws DbException; TransportKeys k) throws DbException;
/**
* Binds the given keys for the given transport to the given contact.
*/
void bindTransportKeys(Transaction txn, ContactId c, TransportId t,
KeySetId k) throws DbException;
/** /**
* Returns true if the database contains the given contact for the given * Returns true if the database contains the given contact for the given
* local pseudonym. * local pseudonym.

View File

@@ -14,6 +14,8 @@ public interface DatabaseConfig {
File getDatabaseDirectory(); File getDatabaseDirectory();
File getDatabaseKeyDirectory();
void setEncryptionKey(SecretKey key); void setEncryptionKey(SecretKey key);
@Nullable @Nullable

View File

@@ -0,0 +1,6 @@
package org.briarproject.bramble.api.plugin;
public interface FileConstants {
String PROP_PATH = "path";
}

View File

@@ -2,8 +2,9 @@ package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.util.Collection; import java.util.Map;
@NotNullByDefault @NotNullByDefault
public interface Plugin { public interface Plugin {
@@ -39,21 +40,19 @@ public interface Plugin {
boolean isRunning(); boolean isRunning();
/** /**
* Returns true if the plugin's {@link #poll(Collection)} method should be * Returns true if the plugin should be polled periodically to attempt to
* called periodically to attempt to establish connections. * establish connections.
*/ */
boolean shouldPoll(); boolean shouldPoll();
/** /**
* Returns the desired interval in milliseconds between calls to the * Returns the desired interval in milliseconds between polling attempts.
* plugin's {@link #poll(Collection)} method.
*/ */
int getPollingInterval(); int getPollingInterval();
/** /**
* Attempts to establish connections to contacts, passing any created * Attempts to establish connections to the given contacts, passing any
* connections to the callback. To avoid creating redundant connections, * created connections to the callback.
* the plugin may exclude the given contacts from polling.
*/ */
void poll(Collection<ContactId> connected); void poll(Map<ContactId, TransportProperties> contacts);
} }

View File

@@ -1,12 +1,9 @@
package org.briarproject.bramble.api.plugin; package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import java.util.Map;
/** /**
* An interface through which a transport plugin interacts with the rest of * An interface through which a transport plugin interacts with the rest of
* the application. * the application.
@@ -25,17 +22,7 @@ public interface PluginCallback {
TransportProperties getLocalProperties(); TransportProperties getLocalProperties();
/** /**
* Returns the plugin's remote transport properties. * Merges the given settings with the plugin's settings
*/
Map<ContactId, TransportProperties> getRemoteProperties();
/**
* Returns the plugin's remote transport properties for the given contact.
*/
TransportProperties getRemoteProperties(ContactId c);
/**
* Merges the given settings with the namespaced settings
*/ */
void mergeSettings(Settings s); void mergeSettings(Settings s);
@@ -45,34 +32,12 @@ public interface PluginCallback {
void mergeLocalProperties(TransportProperties p); void mergeLocalProperties(TransportProperties p);
/** /**
* Presents the user with a choice among two or more named options and * Signals that the transport is enabled.
* returns the user's response. The message may consist of a translatable
* format string and arguments.
*
* @return an index into the array of options indicating the user's choice,
* or -1 if the user cancelled the choice.
*/
int showChoice(String[] options, String... message);
/**
* Asks the user to confirm an action and returns the user's response. The
* message may consist of a translatable format string and arguments.
*/
boolean showConfirmationMessage(String... message);
/**
* Shows a message to the user. The message may consist of a translatable
* format string and arguments.
*/
void showMessage(String... message);
/**
* Signal that the transport got enabled.
*/ */
void transportEnabled(); void transportEnabled();
/** /**
* Signal that the transport got disabled. * Signals that the transport is disabled.
*/ */
void transportDisabled(); void transportDisabled();
} }

View File

@@ -12,4 +12,6 @@ public interface PluginConfig {
Collection<DuplexPluginFactory> getDuplexFactories(); Collection<DuplexPluginFactory> getDuplexFactories();
Collection<SimplexPluginFactory> getSimplexFactories(); Collection<SimplexPluginFactory> getSimplexFactories();
boolean shouldPoll();
} }

View File

@@ -22,11 +22,6 @@ public interface TransportConnectionWriter {
*/ */
int getMaxIdleTime(); int getMaxIdleTime();
/**
* Returns the capacity of the transport connection in bytes.
*/
long getCapacity();
/** /**
* Returns an output stream for writing to the transport connection. * Returns an output stream for writing to the transport connection.
*/ */

View File

@@ -71,11 +71,6 @@ public abstract class AbstractDuplexTransportConnection
return plugin.getMaxIdleTime(); return plugin.getMaxIdleTime();
} }
@Override
public long getCapacity() {
return Long.MAX_VALUE;
}
@Override @Override
public OutputStream getOutputStream() throws IOException { public OutputStream getOutputStream() throws IOException {
return AbstractDuplexTransportConnection.this.getOutputStream(); return AbstractDuplexTransportConnection.this.getOutputStream();

View File

@@ -1,10 +1,10 @@
package org.briarproject.bramble.api.plugin.duplex; package org.briarproject.bramble.api.plugin.duplex;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -15,12 +15,11 @@ import javax.annotation.Nullable;
public interface DuplexPlugin extends Plugin { public interface DuplexPlugin extends Plugin {
/** /**
* Attempts to create and return a connection to the given contact using * Attempts to create and return a connection using the given transport
* the current transport and configuration properties. Returns null if a * properties. Returns null if a connection cannot be created.
* connection cannot be created.
*/ */
@Nullable @Nullable
DuplexTransportConnection createConnection(ContactId c); DuplexTransportConnection createConnection(TransportProperties p);
/** /**
* Returns true if the plugin supports short-range key agreement. * Returns true if the plugin supports short-range key agreement.

View File

@@ -5,7 +5,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
/** /**
* An interface for handling connections created by a duplex transport plugin. * An interface through which a duplex plugin interacts with the rest of the
* application.
*/ */
@NotNullByDefault @NotNullByDefault
public interface DuplexPluginCallback extends PluginCallback { public interface DuplexPluginCallback extends PluginCallback {

View File

@@ -1,10 +1,10 @@
package org.briarproject.bramble.api.plugin.simplex; package org.briarproject.bramble.api.plugin.simplex;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.properties.TransportProperties;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -15,18 +15,16 @@ import javax.annotation.Nullable;
public interface SimplexPlugin extends Plugin { public interface SimplexPlugin extends Plugin {
/** /**
* Attempts to create and return a reader for the given contact using the * Attempts to create and return a reader for the given transport
* current transport and configuration properties. Returns null if a reader * properties. Returns null if a reader cannot be created.
* cannot be created.
*/ */
@Nullable @Nullable
TransportConnectionReader createReader(ContactId c); TransportConnectionReader createReader(TransportProperties p);
/** /**
* Attempts to create and return a writer for the given contact using the * Attempts to create and return a writer for the given transport
* current transport and configuration properties. Returns null if a writer * properties. Returns null if a writer cannot be created.
* cannot be created.
*/ */
@Nullable @Nullable
TransportConnectionWriter createWriter(ContactId c); TransportConnectionWriter createWriter(TransportProperties p);
} }

View File

@@ -7,8 +7,8 @@ import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
/** /**
* An interface for handling readers and writers created by a simplex transport * An interface through which a simplex plugin interacts with the rest of the
* plugin. * application.
*/ */
@NotNullByDefault @NotNullByDefault
public interface SimplexPluginCallback extends PluginCallback { public interface SimplexPluginCallback extends PluginCallback {

View File

@@ -3,10 +3,14 @@ package org.briarproject.bramble.api.reporting;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
@NotNullByDefault @NotNullByDefault
public interface DevConfig { public interface DevConfig {
PublicKey getDevPublicKey(); PublicKey getDevPublicKey();
String getDevOnionAddress(); String getDevOnionAddress();
File getReportDir();
} }

View File

@@ -23,8 +23,6 @@ public interface DevReporter {
/** /**
* Sends any reports previously stored on disk. * Sends any reports previously stored on disk.
*
* @param reportDir the directory where reports are stored.
*/ */
void sendReports(File reportDir); void sendReports();
} }

View File

@@ -2,9 +2,9 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
@NotNullByDefault @NotNullByDefault
public interface SyncSessionFactory { public interface SyncSessionFactory {
@@ -12,8 +12,8 @@ public interface SyncSessionFactory {
SyncSession createIncomingSession(ContactId c, InputStream in); SyncSession createIncomingSession(ContactId c, InputStream in);
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency, SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
OutputStream out); StreamWriter streamWriter);
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency, SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
int maxIdleTime, OutputStream out); int maxIdleTime, StreamWriter streamWriter);
} }

View File

@@ -7,12 +7,12 @@ package org.briarproject.bramble.api.system;
public interface Clock { public interface Clock {
/** /**
* @see {@link System#currentTimeMillis()} * @see System#currentTimeMillis()
*/ */
long currentTimeMillis(); long currentTimeMillis();
/** /**
* @see {@link Thread#sleep(long)} * @see Thread#sleep(long)
*/ */
void sleep(long milliseconds) throws InterruptedException; void sleep(long milliseconds) throws InterruptedException;
} }

View File

@@ -19,48 +19,24 @@ public interface KeyManager {
/** /**
* Informs the key manager that a new contact has been added. Derives and * Informs the key manager that a new contact has been added. Derives and
* stores a set of transport keys for communicating with the contact over * stores a set of transport keys for communicating with the contact over
* each transport. * each transport and returns the key set IDs.
* <p/> * <p/>
* {@link StreamContext StreamContexts} for the contact can be created * {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned. * after this method has returned.
* *
* @param alice true if the local party is Alice * @param alice true if the local party is Alice
* @param active whether the derived keys can be used for outgoing streams
*/ */
void addContact(Transaction txn, ContactId c, SecretKey master, Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
long timestamp, boolean alice) throws DbException; SecretKey master, long timestamp, boolean alice, boolean active)
/**
* Derives and stores a set of unbound transport keys for each transport
* and returns the key set IDs.
* <p/>
* The keys must be bound before they can be used for incoming streams,
* and also activated before they can be used for outgoing streams.
*
* @param alice true if the local party is Alice
*/
Map<TransportId, KeySetId> addUnboundKeys(Transaction txn, SecretKey master,
long timestamp, boolean alice) throws DbException;
/**
* Binds the given transport keys to the given contact.
*/
void bindKeys(Transaction txn, ContactId c, Map<TransportId, KeySetId> keys)
throws DbException; throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. Keys must * Marks the given transport keys as usable for outgoing streams.
* be bound before they are activated.
*/ */
void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys) void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
throws DbException; throws DbException;
/**
* Removes the given transport keys, which must not have been bound, from
* the manager and the database.
*/
void removeKeys(Transaction txn, Map<TransportId, KeySetId> keys)
throws DbException;
/** /**
* Returns true if we have keys that can be used for outgoing streams to * Returns true if we have keys that can be used for outgoing streams to
* the given contact over the given transport. * the given contact over the given transport.

View File

@@ -3,23 +3,20 @@ package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* A set of transport keys for communicating with a contact. If the keys have * A set of transport keys for communicating with a contact.
* not yet been bound to a contact, {@link #getContactId()}} returns null.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class KeySet { public class KeySet {
private final KeySetId keySetId; private final KeySetId keySetId;
@Nullable
private final ContactId contactId; private final ContactId contactId;
private final TransportKeys transportKeys; private final TransportKeys transportKeys;
public KeySet(KeySetId keySetId, @Nullable ContactId contactId, public KeySet(KeySetId keySetId, ContactId contactId,
TransportKeys transportKeys) { TransportKeys transportKeys) {
this.keySetId = keySetId; this.keySetId = keySetId;
this.contactId = contactId; this.contactId = contactId;
@@ -30,7 +27,6 @@ public class KeySet {
return keySetId; return keySetId;
} }
@Nullable
public ContactId getContactId() { public ContactId getContactId() {
return contactId; return contactId;
} }

View File

@@ -0,0 +1,19 @@
package org.briarproject.bramble.api.transport;
import java.io.IOException;
import java.io.OutputStream;
/**
* An interface for writing data to a transport connection. Data will be
* encrypted and authenticated before being written to the connection.
*/
public interface StreamWriter {
OutputStream getOutputStream();
/**
* Sends the end of stream marker, informing the recipient that no more
* data will be sent. The connection is flushed but not closed.
*/
void sendEndOfStream() throws IOException;
}

View File

@@ -12,12 +12,12 @@ public interface StreamWriterFactory {
* Creates an {@link OutputStream OutputStream} for writing to a * Creates an {@link OutputStream OutputStream} for writing to a
* transport stream * transport stream
*/ */
OutputStream createStreamWriter(OutputStream out, StreamContext ctx); StreamWriter createStreamWriter(OutputStream out, StreamContext ctx);
/** /**
* Creates an {@link OutputStream OutputStream} for writing to a contact * Creates an {@link OutputStream OutputStream} for writing to a contact
* exchange stream. * exchange stream.
*/ */
OutputStream createContactExchangeStreamWriter(OutputStream out, StreamWriter createContactExchangeStreamWriter(OutputStream out,
SecretKey headerKey); SecretKey headerKey);
} }

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.api.ui;
public interface UiCallback {
/**
* Presents the user with a choice among two or more named options and
* returns the user's response. The message may consist of a translatable
* format string and arguments.
*
* @return an index into the array of options indicating the user's choice,
* or -1 if the user cancelled the choice.
*/
int showChoice(String[] options, String... message);
/**
* Asks the user to confirm an action and returns the user's response. The
* message may consist of a translatable format string and arguments.
*/
boolean showConfirmationMessage(String... message);
/**
* Shows a message to the user. The message may consist of a translatable
* format string and arguments.
*/
void showMessage(String... message);
}

View File

@@ -9,25 +9,40 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
@NotNullByDefault @NotNullByDefault
public class IoUtils { public class IoUtils {
private static final Logger LOG = Logger.getLogger(IoUtils.class.getName());
public static void deleteFileOrDir(File f) { public static void deleteFileOrDir(File f) {
if (f.isFile()) { if (f.isFile()) {
f.delete(); delete(f);
} else if (f.isDirectory()) { } else if (f.isDirectory()) {
File[] children = f.listFiles(); File[] children = f.listFiles();
if (children != null) if (children == null) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Could not list files in "
+ f.getAbsolutePath());
}
} else {
for (File child : children) deleteFileOrDir(child); for (File child : children) deleteFileOrDir(child);
f.delete(); }
delete(f);
} }
} }
public static void copyAndClose(InputStream in, OutputStream out) private static void delete(File f) {
throws IOException { if (!f.delete() && LOG.isLoggable(WARNING))
LOG.warning("Could not delete " + f.getAbsolutePath());
}
public static void copyAndClose(InputStream in, OutputStream out) {
byte[] buf = new byte[4096]; byte[] buf = new byte[4096];
try { try {
while (true) { while (true) {

View File

@@ -22,19 +22,6 @@ public class OsUtils {
return os != null && os.contains("Mac OS"); return os != null && os.contains("Mac OS");
} }
public static boolean isMacLeopardOrNewer() {
if (!isMac() || version == null) return false;
try {
String[] v = version.split("\\.");
if (v.length != 3) return false;
int major = Integer.parseInt(v[0]);
int minor = Integer.parseInt(v[1]);
return major >= 10 && minor >= 5;
} catch (NumberFormatException e) {
return false;
}
}
public static boolean isLinux() { public static boolean isLinux() {
return os != null && os.contains("Linux") && !isAndroid(); return os != null && os.contains("Linux") && !isAndroid();
} }

View File

@@ -2,6 +2,7 @@ apply plugin: 'java-library'
sourceCompatibility = 1.8 sourceCompatibility = 1.8
targetCompatibility = 1.8 targetCompatibility = 1.8
apply plugin: 'ru.vyarus.animalsniffer'
apply plugin: 'net.ltgt.apt' apply plugin: 'net.ltgt.apt'
apply plugin: 'idea' apply plugin: 'idea'
apply plugin: 'witness' apply plugin: 'witness'
@@ -26,6 +27,8 @@ dependencies {
testImplementation "org.hamcrest:hamcrest-core:1.3" testImplementation "org.hamcrest:hamcrest-core:1.3"
testApt 'com.google.dagger:dagger-compiler:2.0.2' testApt 'com.google.dagger:dagger-compiler:2.0.2'
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
} }
dependencyVerification { dependencyVerification {
@@ -44,6 +47,9 @@ dependencyVerification {
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8', 'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8', 'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb', 'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad', 'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
@@ -52,6 +58,7 @@ dependencyVerification {
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760', 'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16', 'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80', 'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220', 'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
'org.whispersystems:curve25519-java:0.4.1:curve25519-java-0.4.1.jar:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e', 'org.whispersystems:curve25519-java:0.4.1:curve25519-java-0.4.1.jar:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
] ]
@@ -68,8 +75,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
artifacts { artifacts {
testOutput jarTest testOutput jarTest
} }
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
tasks.withType(JavaCompile) {
useJava6StandardLibrary(it)
}

View File

@@ -7,6 +7,7 @@ import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.lifecycle.LifecycleModule; import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule; import org.briarproject.bramble.plugin.PluginModule;
import org.briarproject.bramble.properties.PropertiesModule; import org.briarproject.bramble.properties.PropertiesModule;
import org.briarproject.bramble.reporting.ReportingModule;
import org.briarproject.bramble.sync.SyncModule; import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.system.SystemModule; import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule; import org.briarproject.bramble.transport.TransportModule;
@@ -28,6 +29,8 @@ public interface BrambleCoreEagerSingletons {
void inject(PropertiesModule.EagerSingletons init); void inject(PropertiesModule.EagerSingletons init);
void inject(ReportingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init); void inject(SyncModule.EagerSingletons init);
void inject(SystemModule.EagerSingletons init); void inject(SystemModule.EagerSingletons init);

View File

@@ -59,6 +59,7 @@ public class BrambleCoreModule {
c.inject(new LifecycleModule.EagerSingletons()); c.inject(new LifecycleModule.EagerSingletons());
c.inject(new PluginModule.EagerSingletons()); c.inject(new PluginModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons()); c.inject(new PropertiesModule.EagerSingletons());
c.inject(new ReportingModule.EagerSingletons());
c.inject(new SyncModule.EagerSingletons()); c.inject(new SyncModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons()); c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons()); c.inject(new TransportModule.EagerSingletons());

View File

@@ -30,6 +30,7 @@ import org.briarproject.bramble.api.record.RecordWriter;
import org.briarproject.bramble.api.record.RecordWriterFactory; import org.briarproject.bramble.api.record.RecordWriterFactory;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.StreamReaderFactory; import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory; import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.EOFException; import java.io.EOFException;
@@ -152,11 +153,11 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
recordReaderFactory.createRecordReader(streamReader); recordReaderFactory.createRecordReader(streamReader);
// Create the writers // Create the writers
OutputStream streamWriter = StreamWriter streamWriter =
streamWriterFactory.createContactExchangeStreamWriter(out, streamWriterFactory.createContactExchangeStreamWriter(out,
alice ? aliceHeaderKey : bobHeaderKey); alice ? aliceHeaderKey : bobHeaderKey);
RecordWriter recordWriter = RecordWriter recordWriter =
recordWriterFactory.createRecordWriter(streamWriter); recordWriterFactory.createRecordWriter(streamWriter.getOutputStream());
// Derive the nonces to be signed // Derive the nonces to be signed
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret, byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
@@ -184,8 +185,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
localSignature, localTimestamp); localSignature, localTimestamp);
recordWriter.flush(); recordWriter.flush();
} }
// Close the outgoing stream // Send EOF on the outgoing stream
recordWriter.close(); streamWriter.sendEndOfStream();
// Skip any remaining records from the incoming stream // Skip any remaining records from the incoming stream
try { try {
while (true) recordReader.readRecord(); while (true) recordReader.readRecord();

View File

@@ -46,7 +46,7 @@ class ContactManagerImpl implements ContactManager {
SecretKey master, long timestamp, boolean alice, boolean verified, SecretKey master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException { boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active); ContactId c = db.addContact(txn, remote, local, verified, active);
keyManager.addContact(txn, c, master, timestamp, alice); keyManager.addContact(txn, c, master, timestamp, alice, active);
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact); for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c; return c;

View File

@@ -152,59 +152,47 @@ public class MessageEncrypter {
} }
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) {
if (args.length < 1) { if (args.length < 1) {
printUsage(); printUsage();
return; System.exit(1);
} }
SecureRandom random = new SecureRandom();
MessageEncrypter encrypter = new MessageEncrypter(random);
if (args[0].equals("generate")) { if (args[0].equals("generate")) {
if (args.length != 3) { if (args.length != 3) {
printUsage(); printUsage();
return; System.exit(1);
}
try {
generateKeyPair(args[1], args[2]);
} catch (Exception e) {
e.printStackTrace();
System.exit(2);
} }
// Generate a key pair
KeyPair keyPair = encrypter.generateKeyPair();
PrintStream out = new PrintStream(new FileOutputStream(args[1]));
out.print(
StringUtils.toHexString(keyPair.getPublic().getEncoded()));
out.flush();
out.close();
out = new PrintStream(new FileOutputStream(args[2]));
out.print(
StringUtils.toHexString(keyPair.getPrivate().getEncoded()));
out.flush();
out.close();
} else if (args[0].equals("encrypt")) { } else if (args[0].equals("encrypt")) {
if (args.length != 2) { if (args.length != 2) {
printUsage(); printUsage();
return; System.exit(1);
}
try {
encryptMessage(args[1]);
} catch (Exception e) {
e.printStackTrace();
System.exit(2);
} }
// Encrypt a decrypted message
InputStream in = new FileInputStream(args[1]);
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
PublicKey publicKey =
encrypter.getKeyParser().parsePublicKey(keyBytes);
String message = readFully(System.in);
byte[] plaintext = message.getBytes(Charset.forName("UTF-8"));
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
} else if (args[0].equals("decrypt")) { } else if (args[0].equals("decrypt")) {
if (args.length != 2) { if (args.length != 2) {
printUsage(); printUsage();
return; System.exit(1);
}
try {
decryptMessage(args[1]);
} catch (Exception e) {
e.printStackTrace();
System.exit(2);
} }
// Decrypt an encrypted message
InputStream in = new FileInputStream(args[1]);
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
PrivateKey privateKey =
encrypter.getKeyParser().parsePrivateKey(keyBytes);
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
System.out.println(new String(plaintext, Charset.forName("UTF-8")));
} else { } else {
printUsage(); printUsage();
System.exit(1);
} }
} }
@@ -216,6 +204,46 @@ public class MessageEncrypter {
System.err.println("MessageEncrypter decrypt <private_key_file>"); System.err.println("MessageEncrypter decrypt <private_key_file>");
} }
private static void generateKeyPair(String publicKeyFile,
String privateKeyFile) throws Exception {
SecureRandom random = new SecureRandom();
MessageEncrypter encrypter = new MessageEncrypter(random);
KeyPair keyPair = encrypter.generateKeyPair();
PrintStream out = new PrintStream(new FileOutputStream(publicKeyFile));
out.print(StringUtils.toHexString(keyPair.getPublic().getEncoded()));
out.flush();
out.close();
out = new PrintStream(new FileOutputStream(privateKeyFile));
out.print(StringUtils.toHexString(keyPair.getPrivate().getEncoded()));
out.flush();
out.close();
}
private static void encryptMessage(String publicKeyFile) throws Exception {
SecureRandom random = new SecureRandom();
MessageEncrypter encrypter = new MessageEncrypter(random);
InputStream in = new FileInputStream(publicKeyFile);
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
PublicKey publicKey =
encrypter.getKeyParser().parsePublicKey(keyBytes);
String message = readFully(System.in);
byte[] plaintext = message.getBytes(Charset.forName("UTF-8"));
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
}
private static void decryptMessage(String privateKeyFile) throws Exception {
SecureRandom random = new SecureRandom();
MessageEncrypter encrypter = new MessageEncrypter(random);
InputStream in = new FileInputStream(privateKeyFile);
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
PrivateKey privateKey =
encrypter.getKeyParser().parsePrivateKey(keyBytes);
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
System.out.println(new String(plaintext, Charset.forName("UTF-8")));
}
private static String readFully(InputStream in) throws IOException { private static String readFully(InputStream in) throws IOException {
String newline = System.getProperty("line.separator"); String newline = System.getProperty("line.separator");
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();

View File

@@ -10,37 +10,37 @@ import java.security.GeneralSecurityException;
interface Signature { interface Signature {
/** /**
* @see {@link java.security.Signature#initSign(java.security.PrivateKey)} * @see java.security.Signature#initSign(java.security.PrivateKey)
*/ */
void initSign(PrivateKey k) throws GeneralSecurityException; void initSign(PrivateKey k) throws GeneralSecurityException;
/** /**
* @see {@link java.security.Signature#initVerify(java.security.PublicKey)} * @see java.security.Signature#initVerify(java.security.PublicKey)
*/ */
void initVerify(PublicKey k) throws GeneralSecurityException; void initVerify(PublicKey k) throws GeneralSecurityException;
/** /**
* @see {@link java.security.Signature#update(byte)} * @see java.security.Signature#update(byte)
*/ */
void update(byte b) throws GeneralSecurityException; void update(byte b) throws GeneralSecurityException;
/** /**
* @see {@link java.security.Signature#update(byte[])} * @see java.security.Signature#update(byte[])
*/ */
void update(byte[] b) throws GeneralSecurityException; void update(byte[] b) throws GeneralSecurityException;
/** /**
* @see {@link java.security.Signature#update(byte[], int, int)} * @see java.security.Signature#update(byte[], int, int)
*/ */
void update(byte[] b, int off, int len) throws GeneralSecurityException; void update(byte[] b, int off, int len) throws GeneralSecurityException;
/** /**
* @see {@link java.security.Signature#sign()} * @see java.security.Signature#sign()}
*/ */
byte[] sign() throws GeneralSecurityException; byte[] sign() throws GeneralSecurityException;
/** /**
* @see {@link java.security.Signature#verify(byte[])} * @see java.security.Signature#verify(byte[])
*/ */
boolean verify(byte[] signature) throws GeneralSecurityException; boolean verify(byte[] signature) throws GeneralSecurityException;
} }

View File

@@ -8,6 +8,7 @@ import java.io.InputStream;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_NESTED_LIMIT; import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_NESTED_LIMIT;
@Immutable @Immutable
@@ -16,11 +17,13 @@ class BdfReaderFactoryImpl implements BdfReaderFactory {
@Override @Override
public BdfReader createReader(InputStream in) { public BdfReader createReader(InputStream in) {
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT); return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
DEFAULT_MAX_BUFFER_SIZE);
} }
@Override @Override
public BdfReader createReader(InputStream in, int nestedLimit) { public BdfReader createReader(InputStream in, int nestedLimit,
return new BdfReaderImpl(in, nestedLimit); int maxBufferSize) {
return new BdfReaderImpl(in, nestedLimit, maxBufferSize);
} }
} }

View File

@@ -37,15 +37,16 @@ class BdfReaderImpl implements BdfReader {
private static final byte[] EMPTY_BUFFER = new byte[0]; private static final byte[] EMPTY_BUFFER = new byte[0];
private final InputStream in; private final InputStream in;
private final int nestedLimit; private final int nestedLimit, maxBufferSize;
private boolean hasLookahead = false, eof = false; private boolean hasLookahead = false, eof = false;
private byte next; private byte next;
private byte[] buf = new byte[8]; private byte[] buf = new byte[8];
BdfReaderImpl(InputStream in, int nestedLimit) { BdfReaderImpl(InputStream in, int nestedLimit, int maxBufferSize) {
this.in = in; this.in = in;
this.nestedLimit = nestedLimit; this.nestedLimit = nestedLimit;
this.maxBufferSize = maxBufferSize;
} }
private void readLookahead() throws IOException { private void readLookahead() throws IOException {
@@ -91,8 +92,8 @@ class BdfReaderImpl implements BdfReader {
if (hasBoolean()) return readBoolean(); if (hasBoolean()) return readBoolean();
if (hasLong()) return readLong(); if (hasLong()) return readLong();
if (hasDouble()) return readDouble(); if (hasDouble()) return readDouble();
if (hasString()) return readString(Integer.MAX_VALUE); if (hasString()) return readString();
if (hasRaw()) return readRaw(Integer.MAX_VALUE); if (hasRaw()) return readRaw();
if (hasList()) return readList(level); if (hasList()) return readList(level);
if (hasDictionary()) return readDictionary(level); if (hasDictionary()) return readDictionary(level);
throw new FormatException(); throw new FormatException();
@@ -245,11 +246,11 @@ class BdfReaderImpl implements BdfReader {
} }
@Override @Override
public String readString(int maxLength) throws IOException { public String readString() throws IOException {
if (!hasString()) throw new FormatException(); if (!hasString()) throw new FormatException();
hasLookahead = false; hasLookahead = false;
int length = readStringLength(); int length = readStringLength();
if (length < 0 || length > maxLength) throw new FormatException(); if (length < 0 || length > maxBufferSize) throw new FormatException();
if (length == 0) return ""; if (length == 0) return "";
readIntoBuffer(length); readIntoBuffer(length);
return new String(buf, 0, length, "UTF-8"); return new String(buf, 0, length, "UTF-8");
@@ -279,11 +280,11 @@ class BdfReaderImpl implements BdfReader {
} }
@Override @Override
public byte[] readRaw(int maxLength) throws IOException { public byte[] readRaw() throws IOException {
if (!hasRaw()) throw new FormatException(); if (!hasRaw()) throw new FormatException();
hasLookahead = false; hasLookahead = false;
int length = readRawLength(); int length = readRawLength();
if (length < 0 || length > maxLength) throw new FormatException(); if (length < 0 || length > maxBufferSize) throw new FormatException();
if (length == 0) return EMPTY_BUFFER; if (length == 0) return EMPTY_BUFFER;
byte[] b = new byte[length]; byte[] b = new byte[length];
readIntoBuffer(b, length); readIntoBuffer(b, length);
@@ -381,7 +382,7 @@ class BdfReaderImpl implements BdfReader {
BdfDictionary dictionary = new BdfDictionary(); BdfDictionary dictionary = new BdfDictionary();
readDictionaryStart(); readDictionaryStart();
while (!hasDictionaryEnd()) while (!hasDictionaryEnd())
dictionary.put(readString(Integer.MAX_VALUE), readObject(level + 1)); dictionary.put(readString(), readObject(level + 1));
readDictionaryEnd(); readDictionaryEnd();
return dictionary; return dictionary;
} }

View File

@@ -59,8 +59,8 @@ class MetadataParserImpl implements MetadataParser {
if (reader.hasBoolean()) return reader.readBoolean(); if (reader.hasBoolean()) return reader.readBoolean();
if (reader.hasLong()) return reader.readLong(); if (reader.hasLong()) return reader.readLong();
if (reader.hasDouble()) return reader.readDouble(); if (reader.hasDouble()) return reader.readDouble();
if (reader.hasString()) return reader.readString(Integer.MAX_VALUE); if (reader.hasString()) return reader.readString();
if (reader.hasRaw()) return reader.readRaw(Integer.MAX_VALUE); if (reader.hasRaw()) return reader.readRaw();
if (reader.hasList()) return reader.readList(); if (reader.hasList()) return reader.readList();
if (reader.hasDictionary()) return reader.readDictionary(); if (reader.hasDictionary()) return reader.readDictionary();
throw new FormatException(); throw new FormatException();

View File

@@ -34,8 +34,8 @@ import javax.annotation.Nullable;
* A low-level interface to the database (DatabaseComponent provides a * A low-level interface to the database (DatabaseComponent provides a
* high-level interface). Most operations take a transaction argument, which is * high-level interface). Most operations take a transaction argument, which is
* obtained by calling {@link #startTransaction()}. Every transaction must be * obtained by calling {@link #startTransaction()}. Every transaction must be
* terminated by calling either {@link #abortTransaction(T)} or * terminated by calling either {@link #abortTransaction(Object) abortTransaction(T)} or
* {@link #commitTransaction(T)}, even if an exception is thrown. * {@link #commitTransaction(Object) commitTransaction(T)}, even if an exception is thrown.
*/ */
@NotNullByDefault @NotNullByDefault
interface Database<T> { interface Database<T> {
@@ -125,16 +125,10 @@ interface Database<T> {
throws DbException; throws DbException;
/** /**
* Stores the given transport keys, optionally binding them to the given * Stores the given transport keys for the given contact and returns a
* contact, and returns a key set ID. * key set ID.
*/ */
KeySetId addTransportKeys(T txn, @Nullable ContactId c, TransportKeys k) KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
throws DbException;
/**
* Binds the given keys for the given transport to the given contact.
*/
void bindTransportKeys(T txn, ContactId c, TransportId t, KeySetId k)
throws DbException; throws DbException;
/** /**

View File

@@ -234,27 +234,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public KeySetId addTransportKeys(Transaction transaction, public KeySetId addTransportKeys(Transaction transaction, ContactId c,
@Nullable ContactId c, TransportKeys k) throws DbException { TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (c != null && !db.containsContact(txn, c))
throw new NoSuchContactException();
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
return db.addTransportKeys(txn, c, k);
}
@Override
public void bindTransportKeys(Transaction transaction, ContactId c,
TransportId t, KeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException(); throw new NoSuchTransportException();
db.bindTransportKeys(txn, c, t, k); return db.addTransportKeys(txn, c, k);
} }
@Override @Override

View File

@@ -53,6 +53,7 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static java.sql.Types.INTEGER; import static java.sql.Types.INTEGER;
import static java.util.Collections.singletonList;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.db.Metadata.REMOVE; import static org.briarproject.bramble.api.db.Metadata.REMOVE;
@@ -74,7 +75,7 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
abstract class JdbcDatabase implements Database<Connection> { abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing // Package access for testing
static final int CODE_SCHEMA_VERSION = 38; static final int CODE_SCHEMA_VERSION = 39;
// Rotation period offsets for incoming transport keys // Rotation period offsets for incoming transport keys
private static final int OFFSET_PREV = -1; private static final int OFFSET_PREV = -1;
@@ -236,7 +237,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (transportId _STRING NOT NULL," + " (transportId _STRING NOT NULL,"
+ " keySetId _COUNTER," + " keySetId _COUNTER,"
+ " rotationPeriod BIGINT NOT NULL," + " rotationPeriod BIGINT NOT NULL,"
+ " contactId INT," // Null if keys are not bound + " contactId INT NOT NULL,"
+ " tagKey _SECRET NOT NULL," + " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL," + " headerKey _SECRET NOT NULL,"
+ " stream BIGINT NOT NULL," + " stream BIGINT NOT NULL,"
@@ -255,7 +256,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (transportId _STRING NOT NULL," + " (transportId _STRING NOT NULL,"
+ " keySetId INT NOT NULL," + " keySetId INT NOT NULL,"
+ " rotationPeriod BIGINT NOT NULL," + " rotationPeriod BIGINT NOT NULL,"
+ " contactId INT," // Null if keys are not bound + " contactId INT NOT NULL,"
+ " tagKey _SECRET NOT NULL," + " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL," + " headerKey _SECRET NOT NULL,"
+ " base BIGINT NOT NULL," + " base BIGINT NOT NULL,"
@@ -389,7 +390,7 @@ abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing // Package access for testing
List<Migration<Connection>> getMigrations() { List<Migration<Connection>> getMigrations() {
return Collections.emptyList(); return singletonList(new Migration38_39());
} }
private void storeSchemaVersion(Connection txn, int version) private void storeSchemaVersion(Connection txn, int version)
@@ -883,7 +884,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public KeySetId addTransportKeys(Connection txn, @Nullable ContactId c, public KeySetId addTransportKeys(Connection txn, ContactId c,
TransportKeys k) throws DbException { TransportKeys k) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
@@ -893,8 +894,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " rotationPeriod, tagKey, headerKey, stream, active)" + " rotationPeriod, tagKey, headerKey, stream, active)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
if (c == null) ps.setNull(1, INTEGER); ps.setInt(1, c.getInt());
else ps.setInt(1, c.getInt());
ps.setString(2, k.getTransportId().getString()); ps.setString(2, k.getTransportId().getString());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
ps.setLong(3, outCurr.getRotationPeriod()); ps.setLong(3, outCurr.getRotationPeriod());
@@ -922,8 +922,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, keySetId.getInt()); ps.setInt(1, keySetId.getInt());
if (c == null) ps.setNull(2, INTEGER); ps.setInt(2, c.getInt());
else ps.setInt(2, c.getInt());
ps.setString(3, k.getTransportId().getString()); ps.setString(3, k.getTransportId().getString());
// Previous rotation period // Previous rotation period
IncomingKeys inPrev = k.getPreviousIncomingKeys(); IncomingKeys inPrev = k.getPreviousIncomingKeys();
@@ -965,33 +964,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void bindTransportKeys(Connection txn, ContactId c, TransportId t,
KeySetId k) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE outgoingKeys SET contactId = ?"
+ " WHERE keySetId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, k.getInt());
int affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
sql = "UPDATE incomingKeys SET contactId = ?"
+ " WHERE keySetId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, k.getInt());
affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
@Override @Override
public boolean containsContact(Connection txn, AuthorId remote, public boolean containsContact(Connection txn, AuthorId remote,
AuthorId local) throws DbException { AuthorId local) throws DbException {
@@ -2172,7 +2144,6 @@ abstract class JdbcDatabase implements Database<Connection> {
if (inKeys.size() < (i + 1) * 3) throw new DbStateException(); if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
KeySetId keySetId = new KeySetId(rs.getInt(1)); KeySetId keySetId = new KeySetId(rs.getInt(1));
ContactId contactId = new ContactId(rs.getInt(2)); ContactId contactId = new ContactId(rs.getInt(2));
if (rs.wasNull()) contactId = null;
long rotationPeriod = rs.getLong(3); long rotationPeriod = rs.getLong(3);
SecretKey tagKey = new SecretKey(rs.getBytes(4)); SecretKey tagKey = new SecretKey(rs.getBytes(4));
SecretKey headerKey = new SecretKey(rs.getBytes(5)); SecretKey headerKey = new SecretKey(rs.getBytes(5));

View File

@@ -0,0 +1,54 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
class Migration38_39 implements Migration<Connection> {
private static final Logger LOG =
Logger.getLogger(Migration38_39.class.getName());
@Override
public int getStartVersion() {
return 38;
}
@Override
public int getEndVersion() {
return 39;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
// Add not null constraints
s.execute("ALTER TABLE outgoingKeys"
+ " ALTER COLUMN contactId"
+ " SET NOT NULL");
s.execute("ALTER TABLE incomingKeys"
+ " ALTER COLUMN contactId"
+ " SET NOT NULL");
} catch (SQLException e) {
tryToClose(s);
throw new DbException(e);
}
}
private void tryToClose(@Nullable Statement s) {
try {
if (s != null) s.close();
} catch (SQLException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
}

View File

@@ -14,12 +14,12 @@ import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory; import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory; import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -101,7 +101,7 @@ class ConnectionManagerImpl implements ConnectionManager {
private SyncSession createSimplexOutgoingSession(StreamContext ctx, private SyncSession createSimplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException { TransportConnectionWriter w) throws IOException {
OutputStream streamWriter = streamWriterFactory.createStreamWriter( StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx); w.getOutputStream(), ctx);
return syncSessionFactory.createSimplexOutgoingSession( return syncSessionFactory.createSimplexOutgoingSession(
ctx.getContactId(), w.getMaxLatency(), streamWriter); ctx.getContactId(), w.getMaxLatency(), streamWriter);
@@ -109,7 +109,7 @@ class ConnectionManagerImpl implements ConnectionManager {
private SyncSession createDuplexOutgoingSession(StreamContext ctx, private SyncSession createDuplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException { TransportConnectionWriter w) throws IOException {
OutputStream streamWriter = streamWriterFactory.createStreamWriter( StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx); w.getOutputStream(), ctx);
return syncSessionFactory.createDuplexOutgoingSession( return syncSessionFactory.createDuplexOutgoingSession(
ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(), ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(),
@@ -300,8 +300,8 @@ class ConnectionManagerImpl implements ConnectionManager {
} }
private void disposeReader(boolean exception, boolean recognised) { private void disposeReader(boolean exception, boolean recognised) {
if (exception && outgoingSession != null) // Interrupt the outgoing session so it finishes cleanly
outgoingSession.interrupt(); if (outgoingSession != null) outgoingSession.interrupt();
try { try {
reader.dispose(exception, recognised); reader.dispose(exception, recognised);
} catch (IOException e) { } catch (IOException e) {
@@ -310,6 +310,8 @@ class ConnectionManagerImpl implements ConnectionManager {
} }
private void disposeWriter(boolean exception) { private void disposeWriter(boolean exception) {
// Interrupt the incoming session if an exception occurred,
// otherwise wait for the end of stream marker
if (exception && incomingSession != null) if (exception && incomingSession != null)
incomingSession.interrupt(); incomingSession.interrupt();
try { try {
@@ -407,8 +409,8 @@ class ConnectionManagerImpl implements ConnectionManager {
} }
private void disposeReader(boolean exception, boolean recognised) { private void disposeReader(boolean exception, boolean recognised) {
if (exception && outgoingSession != null) // Interrupt the outgoing session so it finishes cleanly
outgoingSession.interrupt(); if (outgoingSession != null) outgoingSession.interrupt();
try { try {
reader.dispose(exception, recognised); reader.dispose(exception, recognised);
} catch (IOException e) { } catch (IOException e) {
@@ -417,6 +419,8 @@ class ConnectionManagerImpl implements ConnectionManager {
} }
private void disposeWriter(boolean exception) { private void disposeWriter(boolean exception) {
// Interrupt the incoming session if an exception occurred,
// otherwise wait for the end of stream marker
if (exception && incomingSession != null) if (exception && incomingSession != null)
incomingSession.interrupt(); incomingSession.interrupt();
try { try {

View File

@@ -106,7 +106,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
if (m == null) return Collections.emptyList(); if (m == null) return Collections.emptyList();
List<ContactId> ids = new ArrayList<>(m.keySet()); List<ContactId> ids = new ArrayList<>(m.keySet());
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info(ids.size() + " contacts connected"); LOG.info(ids.size() + " contacts connected: " + t);
return ids; return ids;
} finally { } finally {
lock.unlock(); lock.unlock();

View File

@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager; import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
@@ -29,17 +30,19 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.ui.UiCallback; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -57,12 +60,15 @@ class PluginManagerImpl implements PluginManager, Service {
Logger.getLogger(PluginManagerImpl.class.getName()); Logger.getLogger(PluginManagerImpl.class.getName());
private final Executor ioExecutor; private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final EventBus eventBus; private final EventBus eventBus;
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final SettingsManager settingsManager; private final SettingsManager settingsManager;
private final TransportPropertyManager transportPropertyManager; private final TransportPropertyManager transportPropertyManager;
private final UiCallback uiCallback; private final SecureRandom random;
private final Clock clock;
private final Map<TransportId, Plugin> plugins; private final Map<TransportId, Plugin> plugins;
private final List<SimplexPlugin> simplexPlugins; private final List<SimplexPlugin> simplexPlugins;
private final List<DuplexPlugin> duplexPlugins; private final List<DuplexPlugin> duplexPlugins;
@@ -70,27 +76,41 @@ class PluginManagerImpl implements PluginManager, Service {
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@Inject @Inject
PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus, PluginManagerImpl(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler, EventBus eventBus,
PluginConfig pluginConfig, ConnectionManager connectionManager, PluginConfig pluginConfig, ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry,
SettingsManager settingsManager, SettingsManager settingsManager,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
UiCallback uiCallback) { SecureRandom random, Clock clock) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.eventBus = eventBus; this.eventBus = eventBus;
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.transportPropertyManager = transportPropertyManager; this.transportPropertyManager = transportPropertyManager;
this.uiCallback = uiCallback; this.random = random;
this.clock = clock;
plugins = new ConcurrentHashMap<>(); plugins = new ConcurrentHashMap<>();
simplexPlugins = new CopyOnWriteArrayList<>(); simplexPlugins = new CopyOnWriteArrayList<>();
duplexPlugins = new CopyOnWriteArrayList<>(); duplexPlugins = new CopyOnWriteArrayList<>();
startLatches = new ConcurrentHashMap<>(); startLatches = new ConcurrentHashMap<>();
} }
@Override @Override
public void startService() throws ServiceException { public void startService() {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
// Instantiate the poller
if (pluginConfig.shouldPoll()) {
LOG.info("Starting poller");
Poller poller = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, this, transportPropertyManager, random,
clock);
eventBus.addListener(poller);
}
// Instantiate the simplex plugins and start them asynchronously // Instantiate the simplex plugins and start them asynchronously
LOG.info("Starting simplex plugins"); LOG.info("Starting simplex plugins");
for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) { for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) {
@@ -273,26 +293,6 @@ class PluginManagerImpl implements PluginManager, Service {
} }
} }
@Override
public Map<ContactId, TransportProperties> getRemoteProperties() {
try {
return transportPropertyManager.getRemoteProperties(id);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return Collections.emptyMap();
}
}
@Override
public TransportProperties getRemoteProperties(ContactId c) {
try {
return transportPropertyManager.getRemoteProperties(c, id);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return new TransportProperties();
}
}
@Override @Override
public void mergeSettings(Settings s) { public void mergeSettings(Settings s) {
try { try {
@@ -311,21 +311,6 @@ class PluginManagerImpl implements PluginManager, Service {
} }
} }
@Override
public int showChoice(String[] options, String... message) {
return uiCallback.showChoice(options, message);
}
@Override
public boolean showConfirmationMessage(String... message) {
return uiCallback.showConfirmationMessage(message);
}
@Override
public void showMessage(String... message) {
uiCallback.showMessage(message);
}
@Override @Override
public void transportEnabled() { public void transportEnabled() {
eventBus.broadcast(new TransportEnabledEvent(id)); eventBus.broadcast(new TransportEnabledEvent(id));

View File

@@ -1,18 +1,10 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.ConnectionManager; import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -26,8 +18,6 @@ public class PluginModule {
public static class EagerSingletons { public static class EagerSingletons {
@Inject @Inject
PluginManager pluginManager; PluginManager pluginManager;
@Inject
Poller poller;
} }
@Provides @Provides
@@ -35,19 +25,6 @@ public class PluginModule {
return new BackoffFactoryImpl(); return new BackoffFactoryImpl();
} }
@Provides
@Singleton
Poller providePoller(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, PluginManager pluginManager,
SecureRandom random, Clock clock, EventBus eventBus) {
Poller poller = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock);
eventBus.addListener(poller);
return poller;
}
@Provides @Provides
@Singleton @Singleton
ConnectionManager provideConnectionManager( ConnectionManager provideConnectionManager(

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent; import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
@@ -19,12 +20,17 @@ import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent; import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler; import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@@ -33,10 +39,10 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
@@ -49,26 +55,30 @@ class Poller implements EventListener {
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry; private final ConnectionRegistry connectionRegistry;
private final PluginManager pluginManager; private final PluginManager pluginManager;
private final TransportPropertyManager transportPropertyManager;
private final SecureRandom random; private final SecureRandom random;
private final Clock clock; private final Clock clock;
private final Lock lock; private final Lock lock;
private final Map<TransportId, ScheduledPollTask> tasks; // Locking: lock private final Map<TransportId, ScheduledPollTask> tasks; // Locking: lock
private final Set<TransportId> polling; // Locking: lock
@Inject
Poller(@IoExecutor Executor ioExecutor, Poller(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler, @Scheduler ScheduledExecutorService scheduler,
ConnectionManager connectionManager, ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, PluginManager pluginManager, ConnectionRegistry connectionRegistry, PluginManager pluginManager,
TransportPropertyManager transportPropertyManager,
SecureRandom random, Clock clock) { SecureRandom random, Clock clock) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler; this.scheduler = scheduler;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry; this.connectionRegistry = connectionRegistry;
this.pluginManager = pluginManager; this.pluginManager = pluginManager;
this.transportPropertyManager = transportPropertyManager;
this.random = random; this.random = random;
this.clock = clock; this.clock = clock;
lock = new ReentrantLock(); lock = new ReentrantLock();
tasks = new HashMap<>(); tasks = new HashMap<>();
polling = new HashSet<>();
} }
@Override @Override
@@ -120,10 +130,15 @@ class Poller implements EventListener {
private void connectToContact(ContactId c, SimplexPlugin p) { private void connectToContact(ContactId c, SimplexPlugin p) {
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
TransportId t = p.getId(); TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) { if (connectionRegistry.isConnected(c, t)) return;
TransportConnectionWriter w = p.createWriter(c); try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
TransportConnectionWriter w = p.createWriter(props);
if (w != null) if (w != null)
connectionManager.manageOutgoingConnection(c, t, w); connectionManager.manageOutgoingConnection(c, t, w);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
}); });
} }
@@ -131,10 +146,15 @@ class Poller implements EventListener {
private void connectToContact(ContactId c, DuplexPlugin p) { private void connectToContact(ContactId c, DuplexPlugin p) {
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
TransportId t = p.getId(); TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) { if (connectionRegistry.isConnected(c, t)) return;
DuplexTransportConnection d = p.createConnection(c); try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
DuplexTransportConnection d = p.createConnection(props);
if (d != null) if (d != null)
connectionManager.manageOutgoingConnection(c, t, d); connectionManager.manageOutgoingConnection(c, t, d);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
}); });
} }
@@ -186,7 +206,17 @@ class Poller implements EventListener {
private void poll(Plugin p) { private void poll(Plugin p) {
TransportId t = p.getId(); TransportId t = p.getId();
if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t); if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t);
p.poll(connectionRegistry.getConnectedContacts(t)); try {
Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(t);
Collection<ContactId> connected =
connectionRegistry.getConnectedContacts(t);
remote = new HashMap<>(remote);
remote.keySet().removeAll(connected);
if (!remote.isEmpty()) p.poll(remote);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
} }
private class ScheduledPollTask { private class ScheduledPollTask {
@@ -215,20 +245,33 @@ class Poller implements EventListener {
@Override @Override
@IoExecutor @IoExecutor
public void run() { public void run() {
TransportId t = plugin.getId();
boolean shouldPoll;
lock.lock(); lock.lock();
try { try {
TransportId t = plugin.getId();
ScheduledPollTask scheduled = tasks.get(t); ScheduledPollTask scheduled = tasks.get(t);
if (scheduled != null && scheduled.task != this) if (scheduled != null && scheduled.task != this)
return; // Replaced by another task return; // Replaced by another task
tasks.remove(t); tasks.remove(t);
// Don't poll again if last poll is still running
shouldPoll = polling.add(t);
} finally { } finally {
lock.unlock(); lock.unlock();
} }
int delay = plugin.getPollingInterval(); int delay = plugin.getPollingInterval();
if (randomiseNext) delay = (int) (delay * random.nextDouble()); if (randomiseNext) delay = (int) (delay * random.nextDouble());
schedule(plugin, delay, false); schedule(plugin, delay, false);
poll(plugin); if (shouldPoll) {
poll(plugin);
} else if (LOG.isLoggable(INFO)) {
LOG.info("Last poll for " + t + " is still running");
}
lock.lock();
try {
polling.remove(t);
} finally {
lock.unlock();
}
} }
} }
} }

View File

@@ -26,9 +26,10 @@ import org.briarproject.bramble.util.StringUtils;
import java.io.IOException; import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Collection; import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@@ -53,6 +54,12 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(BluetoothPlugin.class.getName()); Logger.getLogger(BluetoothPlugin.class.getName());
/**
* How many milliseconds to pause between connection attempts when
* polling, to avoid interfering with other Bluetooth or wifi connections.
*/
private static final int POLLING_PAUSE_MS = 1000;
final BluetoothConnectionLimiter connectionLimiter; final BluetoothConnectionLimiter connectionLimiter;
private final Executor ioExecutor; private final Executor ioExecutor;
@@ -250,30 +257,42 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
} }
@Override @Override
public void poll(Collection<ContactId> connected) { public void poll(Map<ContactId, TransportProperties> contacts) {
if (!isRunning() || !shouldAllowContactConnections()) return; if (!isRunning() || !shouldAllowContactConnections()) return;
backoff.increment(); backoff.increment();
// Try to connect to known devices in parallel // Try to connect to known devices in a random order
Map<ContactId, TransportProperties> remote = List<ContactId> keys = new ArrayList<>(contacts.keySet());
callback.getRemoteProperties(); Collections.shuffle(keys);
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) { ioExecutor.execute(() -> {
ContactId c = e.getKey(); boolean first = true;
if (connected.contains(c)) continue; for (ContactId c : keys) {
String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue;
String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue;
ioExecutor.execute(() -> {
if (!isRunning() || !shouldAllowContactConnections()) return; if (!isRunning() || !shouldAllowContactConnections()) return;
if (!connectionLimiter.canOpenContactConnection()) return; if (!connectionLimiter.canOpenContactConnection()) return;
TransportProperties p = contacts.get(c);
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue;
String uuid = p.get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue;
// Pause between connection attempts
if (first) {
first = false;
} else {
try {
Thread.sleep(POLLING_PAUSE_MS);
} catch (InterruptedException ex) {
LOG.info("Interrupted while polling");
Thread.currentThread().interrupt();
return;
}
}
DuplexTransportConnection conn = connect(address, uuid); DuplexTransportConnection conn = connect(address, uuid);
if (conn != null) { if (conn != null) {
backoff.reset(); backoff.reset();
if (connectionLimiter.contactConnectionOpened(conn)) if (connectionLimiter.contactConnectionOpened(conn))
callback.outgoingConnectionCreated(c, conn); callback.outgoingConnectionCreated(c, conn);
} }
}); }
} });
} }
@Nullable @Nullable
@@ -308,10 +327,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
} }
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (!isRunning() || !shouldAllowContactConnections()) return null; if (!isRunning() || !shouldAllowContactConnections()) return null;
if (!connectionLimiter.canOpenContactConnection()) return null; if (!connectionLimiter.canOpenContactConnection()) return null;
TransportProperties p = callback.getRemoteProperties(c);
String address = p.get(PROP_ADDRESS); String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null; if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID); String uuid = p.get(PROP_UUID);

View File

@@ -1,27 +1,21 @@
package org.briarproject.bramble.plugin.file; package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.transport.TransportConstants.MIN_STREAM_LENGTH; import static org.briarproject.bramble.api.plugin.FileConstants.PROP_PATH;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@NotNullByDefault @NotNullByDefault
abstract class FilePlugin implements SimplexPlugin { abstract class FilePlugin implements SimplexPlugin {
@@ -29,25 +23,15 @@ abstract class FilePlugin implements SimplexPlugin {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(FilePlugin.class.getName()); Logger.getLogger(FilePlugin.class.getName());
protected final Executor ioExecutor;
protected final SimplexPluginCallback callback; protected final SimplexPluginCallback callback;
protected final int maxLatency; protected final int maxLatency;
protected final AtomicBoolean used = new AtomicBoolean(false);
protected volatile boolean running = false; protected abstract void writerFinished(File f, boolean exception);
@Nullable protected abstract void readerFinished(File f, boolean exception,
protected abstract File chooseOutputDirectory(); boolean recognised);
protected abstract Collection<File> findFilesByName(String filename); FilePlugin(SimplexPluginCallback callback, int maxLatency) {
protected abstract void writerFinished(File f);
protected abstract void readerFinished(File f);
protected FilePlugin(Executor ioExecutor, SimplexPluginCallback callback,
int maxLatency) {
this.ioExecutor = ioExecutor;
this.callback = callback; this.callback = callback;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
} }
@@ -58,81 +42,36 @@ abstract class FilePlugin implements SimplexPlugin {
} }
@Override @Override
public int getMaxIdleTime() { public TransportConnectionReader createReader(TransportProperties p) {
return Integer.MAX_VALUE; // We don't need keepalives if (!isRunning()) return null;
} String path = p.get(PROP_PATH);
if (isNullOrEmpty(path)) return null;
@Override
public boolean isRunning() {
return running;
}
@Override
public TransportConnectionReader createReader(ContactId c) {
return null;
}
@Override
public TransportConnectionWriter createWriter(ContactId c) {
if (!running) return null;
return createWriter(createConnectionFilename());
}
private String createConnectionFilename() {
StringBuilder s = new StringBuilder(12);
for (int i = 0; i < 8; i++) s.append((char) ('a' + Math.random() * 26));
s.append(".dat");
return s.toString();
}
// Package access for testing
boolean isPossibleConnectionFilename(String filename) {
return filename.toLowerCase(Locale.US).matches("[a-z]{8}\\.dat");
}
@Nullable
private TransportConnectionWriter createWriter(String filename) {
if (!running) return null;
File dir = chooseOutputDirectory();
if (dir == null || !dir.exists() || !dir.isDirectory()) return null;
File f = new File(dir, filename);
try { try {
long capacity = dir.getFreeSpace(); File file = new File(path);
if (capacity < MIN_STREAM_LENGTH) return null; FileInputStream in = new FileInputStream(file);
OutputStream out = new FileOutputStream(f); return new FileTransportReader(file, in, this);
return new FileTransportWriter(f, out, capacity, this);
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
f.delete();
return null; return null;
} }
} }
protected void createReaderFromFile(File f) { @Override
if (!running) return; public TransportConnectionWriter createWriter(TransportProperties p) {
ioExecutor.execute(new ReaderCreator(f)); if (!isRunning()) return null;
} String path = p.get(PROP_PATH);
if (isNullOrEmpty(path)) return null;
private class ReaderCreator implements Runnable { try {
File file = new File(path);
private final File file; if (!file.exists() && !file.createNewFile()) {
LOG.info("Failed to create file");
private ReaderCreator(File file) { return null;
this.file = file;
}
@Override
public void run() {
if (isPossibleConnectionFilename(file.getName())) {
try {
FileInputStream in = new FileInputStream(file);
callback.readerCreated(new FileTransportReader(file, in,
FilePlugin.this));
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
} }
FileOutputStream out = new FileOutputStream(file);
return new FileTransportWriter(file, out, this);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} }
} }
} }

View File

@@ -38,9 +38,6 @@ class FileTransportReader implements TransportConnectionReader {
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
if (recognised) { plugin.readerFinished(file, exception, recognised);
file.delete();
plugin.readerFinished(file);
}
} }
} }

View File

@@ -18,14 +18,11 @@ class FileTransportWriter implements TransportConnectionWriter {
private final File file; private final File file;
private final OutputStream out; private final OutputStream out;
private final long capacity;
private final FilePlugin plugin; private final FilePlugin plugin;
FileTransportWriter(File file, OutputStream out, long capacity, FileTransportWriter(File file, OutputStream out, FilePlugin plugin) {
FilePlugin plugin) {
this.file = file; this.file = file;
this.out = out; this.out = out;
this.capacity = capacity;
this.plugin = plugin; this.plugin = plugin;
} }
@@ -39,11 +36,6 @@ class FileTransportWriter implements TransportConnectionWriter {
return plugin.getMaxIdleTime(); return plugin.getMaxIdleTime();
} }
@Override
public long getCapacity() {
return capacity;
}
@Override @Override
public OutputStream getOutputStream() { public OutputStream getOutputStream() {
return out; return out;
@@ -56,7 +48,6 @@ class FileTransportWriter implements TransportConnectionWriter {
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
if (exception) file.delete(); plugin.writerFinished(file, exception);
else plugin.writerFinished(file);
} }
} }

View File

@@ -207,20 +207,16 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
@Override @Override
public void poll(Collection<ContactId> connected) { public void poll(Map<ContactId, TransportProperties> contacts) {
if (!isRunning()) return; if (!isRunning()) return;
backoff.increment(); backoff.increment();
Map<ContactId, TransportProperties> remote = for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
callback.getRemoteProperties(); connectAndCallBack(e.getKey(), e.getValue());
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
} }
} }
private void connectAndCallBack(ContactId c, TransportProperties p) { private void connectAndCallBack(ContactId c, TransportProperties p) {
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
@@ -230,13 +226,8 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (!isRunning()) return null; if (!isRunning()) return null;
return createConnection(callback.getRemoteProperties(c));
}
@Nullable
private DuplexTransportConnection createConnection(TransportProperties p) {
for (InetSocketAddress remote : getRemoteSocketAddresses(p)) { for (InetSocketAddress remote : getRemoteSocketAddresses(p)) {
if (!isConnectable(remote)) { if (!isConnectable(remote)) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {

View File

@@ -1,7 +1,12 @@
package org.briarproject.bramble.reporting; package org.briarproject.bramble.reporting;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.reporting.DevConfig; import org.briarproject.bramble.api.reporting.DevConfig;
import org.briarproject.bramble.api.reporting.DevReporter; import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
@@ -18,17 +23,19 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.net.Socket; import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class DevReporterImpl implements DevReporter { class DevReporterImpl implements DevReporter, EventListener {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(DevReporterImpl.class.getName()); Logger.getLogger(DevReporterImpl.class.getName());
@@ -36,12 +43,15 @@ class DevReporterImpl implements DevReporter {
private static final int SOCKET_TIMEOUT = 30 * 1000; // 30 seconds private static final int SOCKET_TIMEOUT = 30 * 1000; // 30 seconds
private static final int LINE_LENGTH = 70; private static final int LINE_LENGTH = 70;
private final Executor ioExecutor;
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final DevConfig devConfig; private final DevConfig devConfig;
private final SocketFactory torSocketFactory; private final SocketFactory torSocketFactory;
DevReporterImpl(CryptoComponent crypto, DevConfig devConfig, @Inject
SocketFactory torSocketFactory) { DevReporterImpl(@IoExecutor Executor ioExecutor, CryptoComponent crypto,
DevConfig devConfig, SocketFactory torSocketFactory) {
this.ioExecutor = ioExecutor;
this.crypto = crypto; this.crypto = crypto;
this.devConfig = devConfig; this.devConfig = devConfig;
this.torSocketFactory = torSocketFactory; this.torSocketFactory = torSocketFactory;
@@ -63,6 +73,7 @@ class DevReporterImpl implements DevReporter {
@Override @Override
public void encryptReportToFile(File reportDir, String filename, public void encryptReportToFile(File reportDir, String filename,
String report) throws FileNotFoundException { String report) throws FileNotFoundException {
LOG.info("Encrypting report to file");
byte[] plaintext = StringUtils.toUtf8(report); byte[] plaintext = StringUtils.toUtf8(report);
byte[] ciphertext = crypto.encryptToKey(devConfig.getDevPublicKey(), byte[] ciphertext = crypto.encryptToKey(devConfig.getDevPublicKey(),
plaintext); plaintext);
@@ -82,7 +93,17 @@ class DevReporterImpl implements DevReporter {
} }
@Override @Override
public void sendReports(File reportDir) { public void eventOccurred(Event e) {
if (e instanceof TransportEnabledEvent) {
TransportEnabledEvent t = (TransportEnabledEvent) e;
if (t.getTransportId().equals(TorConstants.ID))
ioExecutor.execute(this::sendReports);
}
}
@Override
public void sendReports() {
File reportDir = devConfig.getReportDir();
File[] reports = reportDir.listFiles(); File[] reports = reportDir.listFiles();
if (reports == null || reports.length == 0) if (reports == null || reports.length == 0)
return; // No reports to send return; // No reports to send

View File

@@ -1,10 +1,10 @@
package org.briarproject.bramble.reporting; package org.briarproject.bramble.reporting;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.reporting.DevConfig;
import org.briarproject.bramble.api.reporting.DevReporter; import org.briarproject.bramble.api.reporting.DevReporter;
import javax.net.SocketFactory; import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@@ -12,9 +12,16 @@ import dagger.Provides;
@Module @Module
public class ReportingModule { public class ReportingModule {
public static class EagerSingletons {
@Inject
DevReporter devReporter;
}
@Provides @Provides
DevReporter provideDevReporter(CryptoComponent crypto, @Singleton
DevConfig devConfig, SocketFactory torSocketFactory) { DevReporter provideDevReporter(DevReporterImpl devReporter,
return new DevReporterImpl(crypto, devConfig, torSocketFactory); EventBus eventBus) {
eventBus.addListener(devReporter);
return devReporter;
} }
} }

View File

@@ -23,6 +23,7 @@ import org.briarproject.bramble.api.sync.event.MessageSharedEvent;
import org.briarproject.bramble.api.sync.event.MessageToAckEvent; import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent; import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@@ -67,6 +68,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
private final Clock clock; private final Clock clock;
private final ContactId contactId; private final ContactId contactId;
private final int maxLatency, maxIdleTime; private final int maxLatency, maxIdleTime;
private final StreamWriter streamWriter;
private final SyncRecordWriter recordWriter; private final SyncRecordWriter recordWriter;
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks; private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
@@ -81,7 +83,8 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor, DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, Clock clock, ContactId contactId, int maxLatency, EventBus eventBus, Clock clock, ContactId contactId, int maxLatency,
int maxIdleTime, SyncRecordWriter recordWriter) { int maxIdleTime, StreamWriter streamWriter,
SyncRecordWriter recordWriter) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
@@ -89,6 +92,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
this.contactId = contactId; this.contactId = contactId;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime; this.maxIdleTime = maxIdleTime;
this.streamWriter = streamWriter;
this.recordWriter = recordWriter; this.recordWriter = recordWriter;
writerTasks = new LinkedBlockingQueue<>(); writerTasks = new LinkedBlockingQueue<>();
} }
@@ -149,7 +153,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
dataToFlush = true; dataToFlush = true;
} }
} }
if (dataToFlush) recordWriter.flush(); streamWriter.sendEndOfStream();
} catch (InterruptedException e) { } catch (InterruptedException e) {
LOG.info("Interrupted while waiting for a record to write"); LOG.info("Interrupted while waiting for a record to write");
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();

View File

@@ -63,7 +63,11 @@ class IncomingSession implements SyncSession, EventListener {
eventBus.addListener(this); eventBus.addListener(this);
try { try {
// Read records until interrupted or EOF // Read records until interrupted or EOF
while (!interrupted && !recordReader.eof()) { while (!interrupted) {
if (recordReader.eof()) {
LOG.info("End of stream");
return;
}
if (recordReader.hasAck()) { if (recordReader.hasAck()) {
Ack a = recordReader.readAck(); Ack a = recordReader.readAck();
dbExecutor.execute(new ReceiveAck(a)); dbExecutor.execute(new ReceiveAck(a));

View File

@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.SyncRecordWriter; import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.SyncSession; import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@@ -51,6 +52,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
private final EventBus eventBus; private final EventBus eventBus;
private final ContactId contactId; private final ContactId contactId;
private final int maxLatency; private final int maxLatency;
private final StreamWriter streamWriter;
private final SyncRecordWriter recordWriter; private final SyncRecordWriter recordWriter;
private final AtomicInteger outstandingQueries; private final AtomicInteger outstandingQueries;
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks; private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
@@ -58,13 +60,14 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
private volatile boolean interrupted = false; private volatile boolean interrupted = false;
SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor, SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, ContactId contactId, EventBus eventBus, ContactId contactId, int maxLatency,
int maxLatency, SyncRecordWriter recordWriter) { StreamWriter streamWriter, SyncRecordWriter recordWriter) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.contactId = contactId; this.contactId = contactId;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.streamWriter = streamWriter;
this.recordWriter = recordWriter; this.recordWriter = recordWriter;
outstandingQueries = new AtomicInteger(2); // One per type of record outstandingQueries = new AtomicInteger(2); // One per type of record
writerTasks = new LinkedBlockingQueue<>(); writerTasks = new LinkedBlockingQueue<>();
@@ -85,7 +88,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
if (task == CLOSE) break; if (task == CLOSE) break;
task.run(); task.run();
} }
recordWriter.flush(); streamWriter.sendEndOfStream();
} catch (InterruptedException e) { } catch (InterruptedException e) {
LOG.info("Interrupted while waiting for a record to write"); LOG.info("Interrupted while waiting for a record to write");
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();

View File

@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.sync.SyncRecordWriterFactory;
import org.briarproject.bramble.api.sync.SyncSession; import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory; import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@@ -53,19 +54,21 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
@Override @Override
public SyncSession createSimplexOutgoingSession(ContactId c, public SyncSession createSimplexOutgoingSession(ContactId c,
int maxLatency, OutputStream out) { int maxLatency, StreamWriter streamWriter) {
OutputStream out = streamWriter.getOutputStream();
SyncRecordWriter recordWriter = SyncRecordWriter recordWriter =
recordWriterFactory.createRecordWriter(out); recordWriterFactory.createRecordWriter(out);
return new SimplexOutgoingSession(db, dbExecutor, eventBus, c, return new SimplexOutgoingSession(db, dbExecutor, eventBus, c,
maxLatency, recordWriter); maxLatency, streamWriter, recordWriter);
} }
@Override @Override
public SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency, public SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
int maxIdleTime, OutputStream out) { int maxIdleTime, StreamWriter streamWriter) {
OutputStream out = streamWriter.getOutputStream();
SyncRecordWriter recordWriter = SyncRecordWriter recordWriter =
recordWriterFactory.createRecordWriter(out); recordWriterFactory.createRecordWriter(out);
return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c, return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c,
maxLatency, maxIdleTime, recordWriter); maxLatency, maxIdleTime, streamWriter, recordWriter);
} }
} }

View File

@@ -99,39 +99,18 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
@Override @Override
public void addContact(Transaction txn, ContactId c, SecretKey master, public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
long timestamp, boolean alice) throws DbException { SecretKey master, long timestamp, boolean alice, boolean active)
for (TransportKeyManager m : managers.values())
m.addContact(txn, c, master, timestamp, alice);
}
@Override
public Map<TransportId, KeySetId> addUnboundKeys(Transaction txn,
SecretKey master, long timestamp, boolean alice)
throws DbException { throws DbException {
Map<TransportId, KeySetId> ids = new HashMap<>(); Map<TransportId, KeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) { for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
TransportKeyManager m = e.getValue(); TransportKeyManager m = e.getValue();
ids.put(t, m.addUnboundKeys(txn, master, timestamp, alice)); ids.put(t, m.addContact(txn, c, master, timestamp, alice, active));
} }
return ids; return ids;
} }
@Override
public void bindKeys(Transaction txn, ContactId c,
Map<TransportId, KeySetId> keys) throws DbException {
for (Entry<TransportId, KeySetId> e : keys.entrySet()) {
TransportId t = e.getKey();
TransportKeyManager m = managers.get(t);
if (m == null) {
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
} else {
m.bindKeys(txn, c, e.getValue());
}
}
}
@Override @Override
public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys) public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
throws DbException { throws DbException {
@@ -146,24 +125,10 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
} }
@Override
public void removeKeys(Transaction txn, Map<TransportId, KeySetId> keys)
throws DbException {
for (Entry<TransportId, KeySetId> e : keys.entrySet()) {
TransportId t = e.getKey();
TransportKeyManager m = managers.get(t);
if (m == null) {
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
} else {
m.removeKeys(txn, e.getValue());
}
}
}
@Override @Override
public boolean canSendOutgoingStreams(ContactId c, TransportId t) { public boolean canSendOutgoingStreams(ContactId c, TransportId t) {
TransportKeyManager m = managers.get(t); TransportKeyManager m = managers.get(t);
return m == null ? false : m.canSendOutgoingStreams(c); return m != null && m.canSendOutgoingStreams(c);
} }
@Override @Override

View File

@@ -3,32 +3,28 @@ package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.KeySetId;
import javax.annotation.Nullable; class MutableKeySet {
public class MutableKeySet {
private final KeySetId keySetId; private final KeySetId keySetId;
@Nullable
private final ContactId contactId; private final ContactId contactId;
private final MutableTransportKeys transportKeys; private final MutableTransportKeys transportKeys;
public MutableKeySet(KeySetId keySetId, @Nullable ContactId contactId, MutableKeySet(KeySetId keySetId, ContactId contactId,
MutableTransportKeys transportKeys) { MutableTransportKeys transportKeys) {
this.keySetId = keySetId; this.keySetId = keySetId;
this.contactId = contactId; this.contactId = contactId;
this.transportKeys = transportKeys; this.transportKeys = transportKeys;
} }
public KeySetId getKeySetId() { KeySetId getKeySetId() {
return keySetId; return keySetId;
} }
@Nullable ContactId getContactId() {
public ContactId getContactId() {
return contactId; return contactId;
} }
public MutableTransportKeys getTransportKeys() { MutableTransportKeys getTransportKeys() {
return transportKeys; return transportKeys;
} }
} }

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory; import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory; import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.OutputStream; import java.io.OutputStream;
@@ -23,14 +24,14 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
} }
@Override @Override
public OutputStream createStreamWriter(OutputStream out, public StreamWriter createStreamWriter(OutputStream out,
StreamContext ctx) { StreamContext ctx) {
return new StreamWriterImpl( return new StreamWriterImpl(
streamEncrypterFactory.createStreamEncrypter(out, ctx)); streamEncrypterFactory.createStreamEncrypter(out, ctx));
} }
@Override @Override
public OutputStream createContactExchangeStreamWriter(OutputStream out, public StreamWriter createContactExchangeStreamWriter(OutputStream out,
SecretKey headerKey) { SecretKey headerKey) {
return new StreamWriterImpl( return new StreamWriterImpl(
streamEncrypterFactory.createContactExchangeStreamDecrypter(out, streamEncrypterFactory.createContactExchangeStreamDecrypter(out,

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.crypto.StreamEncrypter; import org.briarproject.bramble.api.crypto.StreamEncrypter;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@@ -17,7 +18,7 @@ import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYL
*/ */
@NotThreadSafe @NotThreadSafe
@NotNullByDefault @NotNullByDefault
class StreamWriterImpl extends OutputStream { class StreamWriterImpl extends OutputStream implements StreamWriter {
private final StreamEncrypter encrypter; private final StreamEncrypter encrypter;
private final byte[] payload; private final byte[] payload;
@@ -29,6 +30,17 @@ class StreamWriterImpl extends OutputStream {
payload = new byte[MAX_PAYLOAD_LENGTH]; payload = new byte[MAX_PAYLOAD_LENGTH];
} }
@Override
public OutputStream getOutputStream() {
return this;
}
@Override
public void sendEndOfStream() throws IOException {
writeFrame(true);
encrypter.flush();
}
@Override @Override
public void close() throws IOException { public void close() throws IOException {
writeFrame(true); writeFrame(true);

View File

@@ -15,18 +15,11 @@ interface TransportKeyManager {
void start(Transaction txn) throws DbException; void start(Transaction txn) throws DbException;
void addContact(Transaction txn, ContactId c, SecretKey master, KeySetId addContact(Transaction txn, ContactId c, SecretKey master,
long timestamp, boolean alice) throws DbException; long timestamp, boolean alice, boolean active) throws DbException;
KeySetId addUnboundKeys(Transaction txn, SecretKey master, long timestamp,
boolean alice) throws DbException;
void bindKeys(Transaction txn, ContactId c, KeySetId k) throws DbException;
void activateKeys(Transaction txn, KeySetId k) throws DbException; void activateKeys(Transaction txn, KeySetId k) throws DbException;
void removeKeys(Transaction txn, KeySetId k) throws DbException;
void removeContact(ContactId c); void removeContact(ContactId c);
boolean canSendOutgoingStreams(ContactId c); boolean canSendOutgoingStreams(ContactId c);

View File

@@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -119,16 +118,14 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
// Locking: lock // Locking: lock
private void addKeys(KeySetId keySetId, @Nullable ContactId contactId, private void addKeys(KeySetId keySetId, ContactId contactId,
MutableTransportKeys m) { MutableTransportKeys m) {
MutableKeySet ks = new MutableKeySet(keySetId, contactId, m); MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
keys.put(keySetId, ks); keys.put(keySetId, ks);
if (contactId != null) { encodeTags(keySetId, contactId, m.getPreviousIncomingKeys());
encodeTags(keySetId, contactId, m.getPreviousIncomingKeys()); encodeTags(keySetId, contactId, m.getCurrentIncomingKeys());
encodeTags(keySetId, contactId, m.getCurrentIncomingKeys()); encodeTags(keySetId, contactId, m.getNextIncomingKeys());
encodeTags(keySetId, contactId, m.getNextIncomingKeys()); considerReplacingOutgoingKeys(ks);
considerReplacingOutgoingKeys(ks);
}
} }
// Locking: lock // Locking: lock
@@ -150,8 +147,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
if (ks.getTransportKeys().getCurrentOutgoingKeys().isActive()) { if (ks.getTransportKeys().getCurrentOutgoingKeys().isActive()) {
MutableKeySet old = outContexts.get(ks.getContactId()); MutableKeySet old = outContexts.get(ks.getContactId());
if (old == null || if (old == null ||
old.getKeySetId().getInt() < ks.getKeySetId().getInt()) old.getKeySetId().getInt() < ks.getKeySetId().getInt()) {
outContexts.put(ks.getContactId(), ks); outContexts.put(ks.getContactId(), ks);
}
} }
} }
@@ -177,20 +175,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public void addContact(Transaction txn, ContactId c, SecretKey master, public KeySetId addContact(Transaction txn, ContactId c, SecretKey master,
long timestamp, boolean alice) throws DbException { long timestamp, boolean alice, boolean active) throws DbException {
deriveAndAddKeys(txn, c, master, timestamp, alice, true);
}
@Override
public KeySetId addUnboundKeys(Transaction txn, SecretKey master,
long timestamp, boolean alice) throws DbException {
return deriveAndAddKeys(txn, null, master, timestamp, alice, false);
}
private KeySetId deriveAndAddKeys(Transaction txn, @Nullable ContactId c,
SecretKey master, long timestamp, boolean alice, boolean active)
throws DbException {
lock.lock(); lock.lock();
try { try {
// Work out what rotation period the timestamp belongs to // Work out what rotation period the timestamp belongs to
@@ -211,31 +197,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
} }
@Override
public void bindKeys(Transaction txn, ContactId c, KeySetId k)
throws DbException {
lock.lock();
try {
MutableKeySet ks = keys.get(k);
if (ks == null) throw new IllegalArgumentException();
// Check that the keys haven't already been bound
if (ks.getContactId() != null) throw new IllegalArgumentException();
MutableTransportKeys m = ks.getTransportKeys();
addKeys(k, c, m);
db.bindTransportKeys(txn, c, m.getTransportId(), k);
} finally {
lock.unlock();
}
}
@Override @Override
public void activateKeys(Transaction txn, KeySetId k) throws DbException { public void activateKeys(Transaction txn, KeySetId k) throws DbException {
lock.lock(); lock.lock();
try { try {
MutableKeySet ks = keys.get(k); MutableKeySet ks = keys.get(k);
if (ks == null) throw new IllegalArgumentException(); if (ks == null) throw new IllegalArgumentException();
// Check that the keys have been bound
if (ks.getContactId() == null) throw new IllegalArgumentException();
MutableTransportKeys m = ks.getTransportKeys(); MutableTransportKeys m = ks.getTransportKeys();
m.getCurrentOutgoingKeys().activate(); m.getCurrentOutgoingKeys().activate();
considerReplacingOutgoingKeys(ks); considerReplacingOutgoingKeys(ks);
@@ -245,21 +212,6 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
} }
@Override
public void removeKeys(Transaction txn, KeySetId k) throws DbException {
lock.lock();
try {
MutableKeySet ks = keys.remove(k);
if (ks == null) throw new IllegalArgumentException();
// Check that the keys haven't been bound
if (ks.getContactId() != null) throw new IllegalArgumentException();
TransportId t = ks.getTransportKeys().getTransportId();
db.removeTransportKeys(txn, t, k);
} finally {
lock.unlock();
}
}
@Override @Override
public void removeContact(ContactId c) { public void removeContact(ContactId c) {
lock.lock(); lock.lock();

View File

@@ -55,8 +55,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
will(returnValue(txn)); will(returnValue(txn));
oneOf(db).addContact(txn, remote, local, verified, active); oneOf(db).addContact(txn, remote, local, verified, active);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(keyManager) oneOf(keyManager).addContact(txn, contactId, master, timestamp,
.addContact(txn, contactId, master, timestamp, alice); alice, active);
oneOf(db).getContact(txn, contactId); oneOf(db).getContact(txn, contactId);
will(returnValue(contact)); will(returnValue(contact));
oneOf(db).commitTransaction(txn); oneOf(db).commitTransaction(txn);

View File

@@ -4,13 +4,16 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.util.StringUtils;
import org.junit.Test; import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE; import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
import static org.briarproject.bramble.data.BdfReaderImpl.DEFAULT_NESTED_LIMIT; import static org.briarproject.bramble.data.BdfReaderImpl.DEFAULT_NESTED_LIMIT;
import static org.briarproject.bramble.util.StringUtils.fromHexString;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.bramble.util.StringUtils.toHexString;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@@ -158,30 +161,32 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testReadString8() throws Exception { public void testReadString8() throws Exception {
String longest = StringUtils.getRandomString(Byte.MAX_VALUE); String longest = getRandomString(Byte.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8")); String longHex = toHexString(longest.getBytes("UTF-8"));
// "foo", the empty string, and 127 random letters // "foo", the empty string, and 127 random letters
setContents("41" + "03" + "666F6F" + "41" + "00" + setContents("41" + "03" + "666F6F" + "41" + "00" +
"41" + "7F" + longHex); "41" + "7F" + longHex);
assertEquals("foo", r.readString(Integer.MAX_VALUE)); assertEquals("foo", r.readString());
assertEquals("", r.readString(Integer.MAX_VALUE)); assertEquals("", r.readString());
assertEquals(longest, r.readString(Integer.MAX_VALUE)); assertEquals(longest, r.readString());
assertTrue(r.eof()); assertTrue(r.eof());
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testReadString8ChecksMaxLength() throws Exception { public void testReadString8ChecksMaxLength() throws Exception {
// "foo" twice int maxBufferSize = 3;
setContents("41" + "03" + "666F6F" + "41" + "03" + "666F6F"); // "foo", "fooo"
assertEquals("foo", r.readString(3)); setContents("41" + "03" + "666F6F"
+ "41" + "04" + "666F6F6F", maxBufferSize);
assertEquals("foo", r.readString());
assertTrue(r.hasString()); assertTrue(r.hasString());
r.readString(2); r.readString();
} }
@Test @Test
public void testSkipString8() throws Exception { public void testSkipString8() throws Exception {
String longest = StringUtils.getRandomString(Byte.MAX_VALUE); String longest = getRandomString(Byte.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8")); String longHex = toHexString(longest.getBytes("UTF-8"));
// "foo", the empty string, and 127 random letters // "foo", the empty string, and 127 random letters
setContents("41" + "03" + "666F6F" + "41" + "00" + setContents("41" + "03" + "666F6F" + "41" + "00" +
"41" + "7F" + longHex); "41" + "7F" + longHex);
@@ -193,34 +198,37 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testReadString16() throws Exception { public void testReadString16() throws Exception {
String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1); String shortest = getRandomString(Byte.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = toHexString(shortest.getBytes("UTF-8"));
String longest = StringUtils.getRandomString(Short.MAX_VALUE); String longest = getRandomString(Short.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8")); String longHex = toHexString(longest.getBytes("UTF-8"));
// 128 random letters and 2^15 -1 random letters // 128 random letters and 2^15 -1 random letters
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex); setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
assertEquals(shortest, r.readString(Integer.MAX_VALUE)); assertEquals(shortest, r.readString());
assertEquals(longest, r.readString(Integer.MAX_VALUE)); assertEquals(longest, r.readString());
assertTrue(r.eof()); assertTrue(r.eof());
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testReadString16ChecksMaxLength() throws Exception { public void testReadString16ChecksMaxLength() throws Exception {
String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1); int maxBufferSize = Byte.MAX_VALUE + 1;
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String valid = getRandomString(Byte.MAX_VALUE + 1);
// 128 random letters, twice String validHex = toHexString(valid.getBytes("UTF-8"));
setContents("42" + "0080" + shortHex + "42" + "0080" + shortHex); String invalidhex = validHex + "20";
assertEquals(shortest, r.readString(Byte.MAX_VALUE + 1)); // 128 random letters, the same plus a space
setContents("42" + "0080" + validHex
+ "42" + "0081" + invalidhex, maxBufferSize);
assertEquals(valid, r.readString());
assertTrue(r.hasString()); assertTrue(r.hasString());
r.readString(Byte.MAX_VALUE); r.readString();
} }
@Test @Test
public void testSkipString16() throws Exception { public void testSkipString16() throws Exception {
String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1); String shortest = getRandomString(Byte.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = toHexString(shortest.getBytes("UTF-8"));
String longest = StringUtils.getRandomString(Short.MAX_VALUE); String longest = getRandomString(Short.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8")); String longHex = toHexString(longest.getBytes("UTF-8"));
// 128 random letters and 2^15 - 1 random letters // 128 random letters and 2^15 - 1 random letters
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex); setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
r.skipString(); r.skipString();
@@ -230,30 +238,32 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testReadString32() throws Exception { public void testReadString32() throws Exception {
String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1); String shortest = getRandomString(Short.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = toHexString(shortest.getBytes("UTF-8"));
// 2^15 random letters // 2^15 random letters
setContents("44" + "00008000" + shortHex); setContents("44" + "00008000" + shortHex);
assertEquals(shortest, r.readString(Integer.MAX_VALUE)); assertEquals(shortest, r.readString());
assertTrue(r.eof()); assertTrue(r.eof());
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testReadString32ChecksMaxLength() throws Exception { public void testReadString32ChecksMaxLength() throws Exception {
String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1); int maxBufferSize = Short.MAX_VALUE + 1;
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String valid = getRandomString(maxBufferSize);
// 2^15 random letters, twice String validHex = toHexString(valid.getBytes("UTF-8"));
setContents("44" + "00008000" + shortHex + String invalidHex = validHex + "20";
"44" + "00008000" + shortHex); // 2^15 random letters, the same plus a space
assertEquals(shortest, r.readString(Short.MAX_VALUE + 1)); setContents("44" + "00008000" + validHex +
"44" + "00008001" + invalidHex, maxBufferSize);
assertEquals(valid, r.readString());
assertTrue(r.hasString()); assertTrue(r.hasString());
r.readString(Short.MAX_VALUE); r.readString();
} }
@Test @Test
public void testSkipString32() throws Exception { public void testSkipString32() throws Exception {
String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1); String shortest = getRandomString(Short.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = toHexString(shortest.getBytes("UTF-8"));
// 2^15 random letters, twice // 2^15 random letters, twice
setContents("44" + "00008000" + shortHex + setContents("44" + "00008000" + shortHex +
"44" + "00008000" + shortHex); "44" + "00008000" + shortHex);
@@ -265,41 +275,43 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testReadUtf8String() throws Exception { public void testReadUtf8String() throws Exception {
String unicode = "\uFDD0\uFDD1\uFDD2\uFDD3"; String unicode = "\uFDD0\uFDD1\uFDD2\uFDD3";
String hex = StringUtils.toHexString(unicode.getBytes("UTF-8")); String hex = toHexString(unicode.getBytes("UTF-8"));
// STRING_8 tag, "foo", the empty string, and the test string // STRING_8 tag, "foo", the empty string, and the test string
setContents("41" + "03" + "666F6F" + "41" + "00" + "41" + "0C" + hex); setContents("41" + "03" + "666F6F" + "41" + "00" + "41" + "0C" + hex);
assertEquals("foo", r.readString(Integer.MAX_VALUE)); assertEquals("foo", r.readString());
assertEquals("", r.readString(Integer.MAX_VALUE)); assertEquals("", r.readString());
assertEquals(unicode, r.readString(Integer.MAX_VALUE)); assertEquals(unicode, r.readString());
assertTrue(r.eof()); assertTrue(r.eof());
} }
@Test @Test
public void testReadRaw8() throws Exception { public void testReadRaw8() throws Exception {
byte[] longest = new byte[Byte.MAX_VALUE]; byte[] longest = new byte[Byte.MAX_VALUE];
String longHex = StringUtils.toHexString(longest); String longHex = toHexString(longest);
// {1, 2, 3}, {}, and 127 zero bytes // {1, 2, 3}, {}, and 127 zero bytes
setContents("51" + "03" + "010203" + "51" + "00" + setContents("51" + "03" + "010203" + "51" + "00" +
"51" + "7F" + longHex); "51" + "7F" + longHex);
assertArrayEquals(new byte[] {1, 2, 3}, r.readRaw(Integer.MAX_VALUE)); assertArrayEquals(new byte[] {1, 2, 3}, r.readRaw());
assertArrayEquals(new byte[0], r.readRaw(Integer.MAX_VALUE)); assertArrayEquals(new byte[0], r.readRaw());
assertArrayEquals(longest, r.readRaw(Integer.MAX_VALUE)); assertArrayEquals(longest, r.readRaw());
assertTrue(r.eof()); assertTrue(r.eof());
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testReadRaw8ChecksMaxLength() throws Exception { public void testReadRaw8ChecksMaxLength() throws Exception {
// {1, 2, 3} twice int maxBufferSize = 3;
setContents("51" + "03" + "010203" + "51" + "03" + "010203"); // {1, 2, 3}, {1, 2, 3, 4}
assertArrayEquals(new byte[] {1, 2, 3}, r.readRaw(3)); setContents("51" + "03" + "010203" + "51" + "04" + "01020304",
maxBufferSize);
assertArrayEquals(new byte[] {1, 2, 3}, r.readRaw());
assertTrue(r.hasRaw()); assertTrue(r.hasRaw());
r.readRaw(2); r.readRaw();
} }
@Test @Test
public void testSkipRaw8() throws Exception { public void testSkipRaw8() throws Exception {
byte[] longest = new byte[Byte.MAX_VALUE]; byte[] longest = new byte[Byte.MAX_VALUE];
String longHex = StringUtils.toHexString(longest); String longHex = toHexString(longest);
// {1, 2, 3}, {}, and 127 zero bytes // {1, 2, 3}, {}, and 127 zero bytes
setContents("51" + "03" + "010203" + "51" + "00" + setContents("51" + "03" + "010203" + "51" + "00" +
"51" + "7F" + longHex); "51" + "7F" + longHex);
@@ -312,33 +324,36 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testReadRaw16() throws Exception { public void testReadRaw16() throws Exception {
byte[] shortest = new byte[Byte.MAX_VALUE + 1]; byte[] shortest = new byte[Byte.MAX_VALUE + 1];
String shortHex = StringUtils.toHexString(shortest); String shortHex = toHexString(shortest);
byte[] longest = new byte[Short.MAX_VALUE]; byte[] longest = new byte[Short.MAX_VALUE];
String longHex = StringUtils.toHexString(longest); String longHex = toHexString(longest);
// 128 zero bytes and 2^15 - 1 zero bytes // 128 zero bytes and 2^15 - 1 zero bytes
setContents("52" + "0080" + shortHex + "52" + "7FFF" + longHex); setContents("52" + "0080" + shortHex + "52" + "7FFF" + longHex);
assertArrayEquals(shortest, r.readRaw(Integer.MAX_VALUE)); assertArrayEquals(shortest, r.readRaw());
assertArrayEquals(longest, r.readRaw(Integer.MAX_VALUE)); assertArrayEquals(longest, r.readRaw());
assertTrue(r.eof()); assertTrue(r.eof());
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testReadRaw16ChecksMaxLength() throws Exception { public void testReadRaw16ChecksMaxLength() throws Exception {
byte[] shortest = new byte[Byte.MAX_VALUE + 1]; int maxBufferSize = Byte.MAX_VALUE + 1;
String shortHex = StringUtils.toHexString(shortest); byte[] valid = new byte[maxBufferSize];
// 128 zero bytes, twice String validHex = toHexString(valid);
setContents("52" + "0080" + shortHex + "52" + "0080" + shortHex); String invalidHex = validHex + "00";
assertArrayEquals(shortest, r.readRaw(Byte.MAX_VALUE + 1)); // 128 zero bytes, 129 zero bytes
setContents("52" + "0080" + validHex
+ "52" + "0081" + invalidHex, maxBufferSize);
assertArrayEquals(valid, r.readRaw());
assertTrue(r.hasRaw()); assertTrue(r.hasRaw());
r.readRaw(Byte.MAX_VALUE); r.readRaw();
} }
@Test @Test
public void testSkipRaw16() throws Exception { public void testSkipRaw16() throws Exception {
byte[] shortest = new byte[Byte.MAX_VALUE + 1]; byte[] shortest = new byte[Byte.MAX_VALUE + 1];
String shortHex = StringUtils.toHexString(shortest); String shortHex = toHexString(shortest);
byte[] longest = new byte[Short.MAX_VALUE]; byte[] longest = new byte[Short.MAX_VALUE];
String longHex = StringUtils.toHexString(longest); String longHex = toHexString(longest);
// 128 zero bytes and 2^15 - 1 zero bytes // 128 zero bytes and 2^15 - 1 zero bytes
setContents("52" + "0080" + shortHex + "52" + "7FFF" + longHex); setContents("52" + "0080" + shortHex + "52" + "7FFF" + longHex);
r.skipRaw(); r.skipRaw();
@@ -349,29 +364,31 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testReadRaw32() throws Exception { public void testReadRaw32() throws Exception {
byte[] shortest = new byte[Short.MAX_VALUE + 1]; byte[] shortest = new byte[Short.MAX_VALUE + 1];
String shortHex = StringUtils.toHexString(shortest); String shortHex = toHexString(shortest);
// 2^15 zero bytes // 2^15 zero bytes
setContents("54" + "00008000" + shortHex); setContents("54" + "00008000" + shortHex);
assertArrayEquals(shortest, r.readRaw(Integer.MAX_VALUE)); assertArrayEquals(shortest, r.readRaw());
assertTrue(r.eof()); assertTrue(r.eof());
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testReadRaw32ChecksMaxLength() throws Exception { public void testReadRaw32ChecksMaxLength() throws Exception {
byte[] shortest = new byte[Short.MAX_VALUE + 1]; int maxBufferSize = Short.MAX_VALUE + 1;
String shortHex = StringUtils.toHexString(shortest); byte[] valid = new byte[maxBufferSize];
// 2^15 zero bytes, twice String validHex = toHexString(valid);
setContents("54" + "00008000" + shortHex + String invalidHex = validHex + "00";
"54" + "00008000" + shortHex); // 2^15 zero bytes, 2^15 + 1 zero bytes
assertArrayEquals(shortest, r.readRaw(Short.MAX_VALUE + 1)); setContents("54" + "00008000" + validHex +
"54" + "00008001" + invalidHex, maxBufferSize);
assertArrayEquals(valid, r.readRaw());
assertTrue(r.hasRaw()); assertTrue(r.hasRaw());
r.readRaw(Short.MAX_VALUE); r.readRaw();
} }
@Test @Test
public void testSkipRaw32() throws Exception { public void testSkipRaw32() throws Exception {
byte[] shortest = new byte[Short.MAX_VALUE + 1]; byte[] shortest = new byte[Short.MAX_VALUE + 1];
String shortHex = StringUtils.toHexString(shortest); String shortHex = toHexString(shortest);
// 2^15 zero bytes, twice // 2^15 zero bytes, twice
setContents("54" + "00008000" + shortHex + setContents("54" + "00008000" + shortHex +
"54" + "00008000" + shortHex); "54" + "00008000" + shortHex);
@@ -393,6 +410,30 @@ public class BdfReaderImplTest extends BrambleTestCase {
assertEquals(NULL_VALUE, list.get(2)); assertEquals(NULL_VALUE, list.get(2));
} }
@Test(expected = FormatException.class)
public void testReadListChecksMaxLengthForString() throws Exception {
// A list containing "foo", a list containing "fooo"
setContents("60" + "41" + "03" + "666F6F" + "80"
+ "60" + "41" + "04" + "666F6F6F" + "80", 3);
BdfList list = r.readList();
assertEquals(1, list.size());
assertEquals("foo", list.get(0));
assertTrue(r.hasList());
r.readList();
}
@Test(expected = FormatException.class)
public void testReadListChecksMaxLengthForRaw() throws Exception {
// A list containing {1, 2, 3}, a list containing {1, 2, 3, 4}
setContents("60" + "51" + "03" + "010203" + "80"
+ "60" + "51" + "04" + "01020304" + "80", 3);
BdfList list = r.readList();
assertEquals(1, list.size());
assertArrayEquals(new byte[] {1, 2, 3}, (byte[]) list.get(0));
assertTrue(r.hasList());
r.readList();
}
@Test @Test
public void testReadListManually() throws Exception { public void testReadListManually() throws Exception {
// A list containing 1, "foo", and null // A list containing 1, "foo", and null
@@ -403,7 +444,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
assertFalse(r.hasListEnd()); assertFalse(r.hasListEnd());
assertEquals(1, r.readLong()); assertEquals(1, r.readLong());
assertFalse(r.hasListEnd()); assertFalse(r.hasListEnd());
assertEquals("foo", r.readString(1000)); assertEquals("foo", r.readString());
assertFalse(r.hasListEnd()); assertFalse(r.hasListEnd());
assertTrue(r.hasNull()); assertTrue(r.hasNull());
r.readNull(); r.readNull();
@@ -435,6 +476,47 @@ public class BdfReaderImplTest extends BrambleTestCase {
assertEquals(NULL_VALUE, dictionary.get("bar")); assertEquals(NULL_VALUE, dictionary.get("bar"));
} }
@Test(expected = FormatException.class)
public void testReadDictionaryChecksMaxLengthForKey() throws Exception {
// A dictionary containing "foo" -> null, a dictionary containing
// "fooo" -> null
setContents("70" + "41" + "03" + "666F6F" + "00" + "80"
+ "70" + "41" + "04" + "666F6F6F" + "00" + "80", 3);
BdfDictionary dictionary = r.readDictionary();
assertEquals(1, dictionary.size());
assertEquals(NULL_VALUE, dictionary.get("foo"));
assertTrue(r.hasDictionary());
r.readDictionary();
}
@Test(expected = FormatException.class)
public void testReadDictionaryChecksMaxLengthForString() throws Exception {
// A dictionary containing "foo" -> "bar", a dictionary containing
// "foo" -> "baar"
String foo = "41" + "03" + "666F6F";
setContents("70" + foo + "41" + "03" + "626172" + "80"
+ "70" + foo + "41" + "04" + "62616172" + "80", 3);
BdfDictionary dictionary = r.readDictionary();
assertEquals(1, dictionary.size());
assertEquals("bar", dictionary.get("foo"));
assertTrue(r.hasDictionary());
r.readDictionary();
}
@Test(expected = FormatException.class)
public void testReadDictionaryChecksMaxLengthForRaw() throws Exception {
// A dictionary containing "foo" -> {1, 2, 3}, a dictionary containing
// "foo" -> {1, 2, 3, 4}
String foo = "41" + "03" + "666F6F";
setContents("70" + foo + "51" + "03" + "010203" + "80"
+ "70" + foo + "51" + "04" + "01020304" + "80", 3);
BdfDictionary dictionary = r.readDictionary();
assertEquals(1, dictionary.size());
assertArrayEquals(new byte[] {1, 2, 3}, (byte[]) dictionary.get("foo"));
assertTrue(r.hasDictionary());
r.readDictionary();
}
@Test @Test
public void testReadDictionaryManually() throws Exception { public void testReadDictionaryManually() throws Exception {
// A dictionary containing "foo" -> 123 and "bar" -> null // A dictionary containing "foo" -> 123 and "bar" -> null
@@ -442,11 +524,11 @@ public class BdfReaderImplTest extends BrambleTestCase {
"41" + "03" + "626172" + "00" + "80"); "41" + "03" + "626172" + "00" + "80");
r.readDictionaryStart(); r.readDictionaryStart();
assertFalse(r.hasDictionaryEnd()); assertFalse(r.hasDictionaryEnd());
assertEquals("foo", r.readString(1000)); assertEquals("foo", r.readString());
assertFalse(r.hasDictionaryEnd()); assertFalse(r.hasDictionaryEnd());
assertEquals(123, r.readLong()); assertEquals(123, r.readLong());
assertFalse(r.hasDictionaryEnd()); assertFalse(r.hasDictionaryEnd());
assertEquals("bar", r.readString(1000)); assertEquals("bar", r.readString());
assertFalse(r.hasDictionaryEnd()); assertFalse(r.hasDictionaryEnd());
assertTrue(r.hasNull()); assertTrue(r.hasNull());
r.readNull(); r.readNull();
@@ -537,8 +619,11 @@ public class BdfReaderImplTest extends BrambleTestCase {
} }
private void setContents(String hex) { private void setContents(String hex) {
ByteArrayInputStream in = new ByteArrayInputStream( setContents(hex, DEFAULT_MAX_BUFFER_SIZE);
StringUtils.fromHexString(hex)); }
r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT);
private void setContents(String hex, int maxBufferSize) {
ByteArrayInputStream in = new ByteArrayInputStream(fromHexString(hex));
r = new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT, maxBufferSize);
} }
} }

View File

@@ -289,11 +289,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not) // Check whether the contact is in the DB (which it's not)
exactly(17).of(database).startTransaction(); exactly(16).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(17).of(database).containsContact(txn, contactId); exactly(16).of(database).containsContact(txn, contactId);
will(returnValue(false)); will(returnValue(false));
exactly(17).of(database).abortTransaction(txn); exactly(16).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
@@ -308,16 +308,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(false);
try {
db.bindTransportKeys(transaction, contactId, transportId, keySetId);
fail();
} catch (NoSuchContactException expected) {
// Expected
} finally {
db.endTransaction(transaction);
}
transaction = db.startTransaction(false); transaction = db.startTransaction(false);
try { try {
db.generateAck(transaction, contactId, 123); db.generateAck(transaction, contactId, 123);
@@ -773,13 +763,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// endTransaction() // endTransaction()
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
// Check whether the transport is in the DB (which it's not) // Check whether the transport is in the DB (which it's not)
exactly(6).of(database).startTransaction(); exactly(5).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).containsContact(txn, contactId); exactly(5).of(database).containsTransport(txn, transportId);
will(returnValue(true));
exactly(6).of(database).containsTransport(txn, transportId);
will(returnValue(false)); will(returnValue(false));
exactly(6).of(database).abortTransaction(txn); exactly(5).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
@@ -794,16 +782,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction(false);
try {
db.bindTransportKeys(transaction, contactId, transportId, keySetId);
fail();
} catch (NoSuchTransportException expected) {
// Expected
} finally {
db.endTransaction(transaction);
}
transaction = db.startTransaction(false); transaction = db.startTransaction(false);
try { try {
db.getTransportKeys(transaction, transportId); db.getTransportKeys(transaction, transportId);

View File

@@ -94,6 +94,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
private final TransportId transportId; private final TransportId transportId;
private final ContactId contactId; private final ContactId contactId;
private final KeySetId keySetId, keySetId1; private final KeySetId keySetId, keySetId1;
private final Random random = new Random();
JdbcDatabaseTest() throws Exception { JdbcDatabaseTest() throws Exception {
clientId = getClientId(); clientId = getClientId();
@@ -670,8 +671,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
@Test @Test
public void testTransportKeys() throws Exception { public void testTransportKeys() throws Exception {
long rotationPeriod = 123, rotationPeriod1 = 234; long rotationPeriod = 123, rotationPeriod1 = 234;
TransportKeys keys = createTransportKeys(rotationPeriod); boolean active = random.nextBoolean();
TransportKeys keys1 = createTransportKeys(rotationPeriod1); TransportKeys keys = createTransportKeys(rotationPeriod, active);
TransportKeys keys1 = createTransportKeys(rotationPeriod1, active);
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
@@ -682,7 +684,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add the contact, the transport and the transport keys // Add the contact, the transport and the transport keys
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(), assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true)); true, active));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1)); assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
@@ -701,8 +703,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
} }
// Rotate the transport keys // Rotate the transport keys
TransportKeys rotated = createTransportKeys(rotationPeriod + 1); TransportKeys rotated = createTransportKeys(rotationPeriod + 1, active);
TransportKeys rotated1 = createTransportKeys(rotationPeriod1 + 1); TransportKeys rotated1 =
createTransportKeys(rotationPeriod1 + 1, active);
db.updateTransportKeys(txn, new KeySet(keySetId, contactId, rotated)); db.updateTransportKeys(txn, new KeySet(keySetId, contactId, rotated));
db.updateTransportKeys(txn, new KeySet(keySetId1, contactId, rotated1)); db.updateTransportKeys(txn, new KeySet(keySetId1, contactId, rotated1));
@@ -727,95 +730,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testUnboundTransportKeys() throws Exception {
long rotationPeriod = 123, rotationPeriod1 = 234;
TransportKeys keys = createTransportKeys(rotationPeriod);
TransportKeys keys1 = createTransportKeys(rotationPeriod1);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Initially there should be no transport keys in the database
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
// Add the contact, the transport and the unbound transport keys
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, null, keys));
assertEquals(keySetId1, db.addTransportKeys(txn, null, keys1));
// Retrieve the transport keys
Collection<KeySet> allKeys = db.getTransportKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (KeySet ks : allKeys) {
assertNull(ks.getContactId());
if (ks.getKeySetId().equals(keySetId)) {
assertKeysEquals(keys, ks.getTransportKeys());
} else {
assertEquals(keySetId1, ks.getKeySetId());
assertKeysEquals(keys1, ks.getTransportKeys());
}
}
// Bind the first set of transport keys
db.bindTransportKeys(txn, contactId, transportId, keySetId);
// Retrieve the keys again - the first set should be bound
allKeys = db.getTransportKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (KeySet ks : allKeys) {
if (ks.getKeySetId().equals(keySetId)) {
assertEquals(contactId, ks.getContactId());
assertKeysEquals(keys, ks.getTransportKeys());
} else {
assertEquals(keySetId1, ks.getKeySetId());
assertNull(ks.getContactId());
assertKeysEquals(keys1, ks.getTransportKeys());
}
}
// Rotate the transport keys
TransportKeys rotated = createTransportKeys(rotationPeriod + 1);
TransportKeys rotated1 = createTransportKeys(rotationPeriod1 + 1);
db.updateTransportKeys(txn, new KeySet(keySetId, contactId, rotated));
db.updateTransportKeys(txn, new KeySet(keySetId1, null, rotated1));
// Retrieve the transport keys again
allKeys = db.getTransportKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (KeySet ks : allKeys) {
if (ks.getKeySetId().equals(keySetId)) {
assertEquals(contactId, ks.getContactId());
assertKeysEquals(rotated, ks.getTransportKeys());
} else {
assertEquals(keySetId1, ks.getKeySetId());
assertNull(ks.getContactId());
assertKeysEquals(rotated1, ks.getTransportKeys());
}
}
// Remove the unbound transport keys
db.removeTransportKeys(txn, transportId, keySetId1);
// Retrieve the keys again - the second set should be gone
allKeys = db.getTransportKeys(txn, transportId);
assertEquals(1, allKeys.size());
KeySet ks = allKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId());
assertKeysEquals(rotated, ks.getTransportKeys());
// Removing the transport should remove the remaining transport keys
db.removeTransport(txn, transportId);
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
db.commitTransaction(txn);
db.close();
}
private void assertKeysEquals(TransportKeys expected, private void assertKeysEquals(TransportKeys expected,
TransportKeys actual) { TransportKeys actual) {
assertEquals(expected.getTransportId(), actual.getTransportId()); assertEquals(expected.getTransportId(), actual.getTransportId());
@@ -853,7 +767,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
@Test @Test
public void testIncrementStreamCounter() throws Exception { public void testIncrementStreamCounter() throws Exception {
long rotationPeriod = 123; long rotationPeriod = 123;
TransportKeys keys = createTransportKeys(rotationPeriod); TransportKeys keys = createTransportKeys(rotationPeriod, true);
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter(); long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
Database<Connection> db = open(false); Database<Connection> db = open(false);
@@ -893,8 +807,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
@Test @Test
public void testSetReorderingWindow() throws Exception { public void testSetReorderingWindow() throws Exception {
boolean active = random.nextBoolean();
long rotationPeriod = 123; long rotationPeriod = 123;
TransportKeys keys = createTransportKeys(rotationPeriod); TransportKeys keys = createTransportKeys(rotationPeriod, active);
long base = keys.getCurrentIncomingKeys().getWindowBase(); long base = keys.getCurrentIncomingKeys().getWindowBase();
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap(); byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
@@ -904,12 +819,12 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
// Add the contact, transport and transport keys // Add the contact, transport and transport keys
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(), assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true)); true, active));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
// Update the reordering window and retrieve the transport keys // Update the reordering window and retrieve the transport keys
new Random().nextBytes(bitmap); random.nextBytes(bitmap);
db.setReorderingWindow(txn, keySetId, transportId, rotationPeriod, db.setReorderingWindow(txn, keySetId, transportId, rotationPeriod,
base + 1, bitmap); base + 1, bitmap);
Collection<KeySet> newKeys = db.getTransportKeys(txn, transportId); Collection<KeySet> newKeys = db.getTransportKeys(txn, transportId);
@@ -1908,7 +1823,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
return db; return db;
} }
private TransportKeys createTransportKeys(long rotationPeriod) { private TransportKeys createTransportKeys(long rotationPeriod,
boolean active) {
SecretKey inPrevTagKey = getSecretKey(); SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey(); SecretKey inPrevHeaderKey = getSecretKey();
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey, IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
@@ -1924,7 +1840,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
SecretKey outCurrTagKey = getSecretKey(); SecretKey outCurrTagKey = getSecretKey();
SecretKey outCurrHeaderKey = getSecretKey(); SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey, OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
rotationPeriod, 456, true); rotationPeriod, 456, active);
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr); return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
} }

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.ConnectionManager; import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginException; import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -13,16 +14,18 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.ui.UiCallback; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.jmock.lib.concurrent.Synchroniser; import org.jmock.lib.concurrent.Synchroniser;
import org.junit.Test; import org.junit.Test;
import java.security.SecureRandom;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -34,15 +37,20 @@ public class PluginManagerImplTest extends BrambleTestCase {
setThreadingPolicy(new Synchroniser()); setThreadingPolicy(new Synchroniser());
}}; }};
Executor ioExecutor = Executors.newSingleThreadExecutor(); Executor ioExecutor = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
SecureRandom random = new SecureRandom();
Clock clock = context.mock(Clock.class);
EventBus eventBus = context.mock(EventBus.class); EventBus eventBus = context.mock(EventBus.class);
PluginConfig pluginConfig = context.mock(PluginConfig.class); PluginConfig pluginConfig = context.mock(PluginConfig.class);
ConnectionManager connectionManager = ConnectionManager connectionManager =
context.mock(ConnectionManager.class); context.mock(ConnectionManager.class);
ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
SettingsManager settingsManager = SettingsManager settingsManager =
context.mock(SettingsManager.class); context.mock(SettingsManager.class);
TransportPropertyManager transportPropertyManager = TransportPropertyManager transportPropertyManager =
context.mock(TransportPropertyManager.class); context.mock(TransportPropertyManager.class);
UiCallback uiCallback = context.mock(UiCallback.class);
// Two simplex plugin factories: both create plugins, one fails to start // Two simplex plugin factories: both create plugins, one fails to start
SimplexPluginFactory simplexFactory = SimplexPluginFactory simplexFactory =
@@ -71,6 +79,8 @@ public class PluginManagerImplTest extends BrambleTestCase {
will(returnValue(simplexFailId)); will(returnValue(simplexFailId));
allowing(duplexPlugin).getId(); allowing(duplexPlugin).getId();
will(returnValue(duplexId)); will(returnValue(duplexId));
allowing(pluginConfig).shouldPoll();
will(returnValue(false));
// start() // start()
// First simplex plugin // First simplex plugin
oneOf(pluginConfig).getSimplexFactories(); oneOf(pluginConfig).getSimplexFactories();
@@ -112,9 +122,9 @@ public class PluginManagerImplTest extends BrambleTestCase {
oneOf(duplexPlugin).stop(); oneOf(duplexPlugin).stop();
}}); }});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus, PluginManagerImpl p = new PluginManagerImpl(ioExecutor, scheduler,
pluginConfig, connectionManager, settingsManager, eventBus, pluginConfig, connectionManager, connectionRegistry,
transportPropertyManager, uiCallback); settingsManager, transportPropertyManager, random, clock);
// Two plugins should be started and stopped // Two plugins should be started and stopped
p.startService(); p.startService();

View File

@@ -15,6 +15,8 @@ import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent; import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin; import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor; import org.briarproject.bramble.test.ImmediateExecutor;
@@ -24,13 +26,15 @@ import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test; import org.junit.Test;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
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.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -44,6 +48,8 @@ public class PollerTest extends BrambleMockTestCase {
context.mock(ConnectionRegistry.class); context.mock(ConnectionRegistry.class);
private final PluginManager pluginManager = private final PluginManager pluginManager =
context.mock(PluginManager.class); context.mock(PluginManager.class);
private final TransportPropertyManager transportPropertyManager =
context.mock(TransportPropertyManager.class);
private final Clock clock = context.mock(Clock.class); private final Clock clock = context.mock(Clock.class);
private final ScheduledFuture future = context.mock(ScheduledFuture.class); private final ScheduledFuture future = context.mock(ScheduledFuture.class);
private final SecureRandom random; private final SecureRandom random;
@@ -51,6 +57,7 @@ public class PollerTest extends BrambleMockTestCase {
private final Executor ioExecutor = new ImmediateExecutor(); private final Executor ioExecutor = new ImmediateExecutor();
private final TransportId transportId = getTransportId(); private final TransportId transportId = getTransportId();
private final ContactId contactId = new ContactId(234); private final ContactId contactId = new ContactId(234);
private final TransportProperties properties = new TransportProperties();
private final int pollingInterval = 60 * 1000; private final int pollingInterval = 60 * 1000;
private final long now = System.currentTimeMillis(); private final long now = System.currentTimeMillis();
@@ -66,8 +73,8 @@ public class PollerTest extends BrambleMockTestCase {
SimplexPlugin simplexPlugin1 = SimplexPlugin simplexPlugin1 =
context.mock(SimplexPlugin.class, "simplexPlugin1"); context.mock(SimplexPlugin.class, "simplexPlugin1");
TransportId simplexId1 = getTransportId(); TransportId simplexId1 = getTransportId();
List<SimplexPlugin> simplexPlugins = Arrays.asList(simplexPlugin, List<SimplexPlugin> simplexPlugins =
simplexPlugin1); asList(simplexPlugin, simplexPlugin1);
TransportConnectionWriter simplexWriter = TransportConnectionWriter simplexWriter =
context.mock(TransportConnectionWriter.class); context.mock(TransportConnectionWriter.class);
@@ -76,8 +83,8 @@ public class PollerTest extends BrambleMockTestCase {
TransportId duplexId = getTransportId(); TransportId duplexId = getTransportId();
DuplexPlugin duplexPlugin1 = DuplexPlugin duplexPlugin1 =
context.mock(DuplexPlugin.class, "duplexPlugin1"); context.mock(DuplexPlugin.class, "duplexPlugin1");
List<DuplexPlugin> duplexPlugins = Arrays.asList(duplexPlugin, List<DuplexPlugin> duplexPlugins =
duplexPlugin1); asList(duplexPlugin, duplexPlugin1);
DuplexTransportConnection duplexConnection = DuplexTransportConnection duplexConnection =
context.mock(DuplexTransportConnection.class); context.mock(DuplexTransportConnection.class);
@@ -96,8 +103,12 @@ public class PollerTest extends BrambleMockTestCase {
will(returnValue(simplexId1)); will(returnValue(simplexId1));
oneOf(connectionRegistry).isConnected(contactId, simplexId1); oneOf(connectionRegistry).isConnected(contactId, simplexId1);
will(returnValue(false)); will(returnValue(false));
// Get the transport properties
oneOf(transportPropertyManager).getRemoteProperties(contactId,
simplexId1);
will(returnValue(properties));
// Connect to the contact // Connect to the contact
oneOf(simplexPlugin1).createWriter(contactId); oneOf(simplexPlugin1).createWriter(properties);
will(returnValue(simplexWriter)); will(returnValue(simplexWriter));
// Pass the connection to the connection manager // Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId, oneOf(connectionManager).manageOutgoingConnection(contactId,
@@ -105,7 +116,7 @@ public class PollerTest extends BrambleMockTestCase {
// Get the duplex plugins // Get the duplex plugins
oneOf(pluginManager).getDuplexPlugins(); oneOf(pluginManager).getDuplexPlugins();
will(returnValue(duplexPlugins)); will(returnValue(duplexPlugins));
// The first plugin supports polling // The duplex plugin supports polling
oneOf(duplexPlugin).shouldPoll(); oneOf(duplexPlugin).shouldPoll();
will(returnValue(true)); will(returnValue(true));
// Check whether the contact is already connected // Check whether the contact is already connected
@@ -113,8 +124,12 @@ public class PollerTest extends BrambleMockTestCase {
will(returnValue(duplexId)); will(returnValue(duplexId));
oneOf(connectionRegistry).isConnected(contactId, duplexId); oneOf(connectionRegistry).isConnected(contactId, duplexId);
will(returnValue(false)); will(returnValue(false));
// Get the transport properties
oneOf(transportPropertyManager).getRemoteProperties(contactId,
duplexId);
will(returnValue(properties));
// Connect to the contact // Connect to the contact
oneOf(duplexPlugin).createConnection(contactId); oneOf(duplexPlugin).createConnection(properties);
will(returnValue(duplexConnection)); will(returnValue(duplexConnection));
// Pass the connection to the connection manager // Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId, oneOf(connectionManager).manageOutgoingConnection(contactId,
@@ -125,7 +140,8 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ContactStatusChangedEvent(contactId, true)); p.eventOccurred(new ContactStatusChangedEvent(contactId, true));
} }
@@ -165,8 +181,12 @@ public class PollerTest extends BrambleMockTestCase {
// Check whether the contact is already connected // Check whether the contact is already connected
oneOf(connectionRegistry).isConnected(contactId, transportId); oneOf(connectionRegistry).isConnected(contactId, transportId);
will(returnValue(false)); will(returnValue(false));
// Get the transport properties
oneOf(transportPropertyManager).getRemoteProperties(contactId,
transportId);
will(returnValue(properties));
// Connect to the contact // Connect to the contact
oneOf(plugin).createConnection(contactId); oneOf(plugin).createConnection(properties);
will(returnValue(duplexConnection)); will(returnValue(duplexConnection));
// Pass the connection to the connection manager // Pass the connection to the connection manager
oneOf(connectionManager).manageOutgoingConnection(contactId, oneOf(connectionManager).manageOutgoingConnection(contactId,
@@ -174,15 +194,15 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionClosedEvent(contactId, transportId, p.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
false)); false));
} }
@Test @Test
public void testRescheduleOnConnectionOpened() throws Exception { public void testRescheduleOnConnectionOpened() {
Plugin plugin = context.mock(Plugin.class); Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -205,14 +225,15 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
} }
@Test @Test
public void testRescheduleDoesNotReplaceEarlierTask() throws Exception { public void testRescheduleDoesNotReplaceEarlierTask() {
Plugin plugin = context.mock(Plugin.class); Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -248,7 +269,8 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
@@ -257,7 +279,7 @@ public class PollerTest extends BrambleMockTestCase {
} }
@Test @Test
public void testRescheduleReplacesLaterTask() throws Exception { public void testRescheduleReplacesLaterTask() {
Plugin plugin = context.mock(Plugin.class); Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -296,7 +318,8 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
@@ -306,8 +329,7 @@ public class PollerTest extends BrambleMockTestCase {
@Test @Test
public void testPollsOnTransportEnabled() throws Exception { public void testPollsOnTransportEnabled() throws Exception {
Plugin plugin = context.mock(Plugin.class); DuplexPlugin plugin = context.mock(DuplexPlugin.class);
List<ContactId> connected = Collections.singletonList(contactId);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
allowing(plugin).getId(); allowing(plugin).getId();
@@ -335,20 +357,69 @@ public class PollerTest extends BrambleMockTestCase {
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) (pollingInterval * 0.5)), with(MILLISECONDS)); with((long) (pollingInterval * 0.5)), with(MILLISECONDS));
will(returnValue(future)); will(returnValue(future));
// Poll the plugin // Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties)));
oneOf(connectionRegistry).getConnectedContacts(transportId); oneOf(connectionRegistry).getConnectedContacts(transportId);
will(returnValue(connected)); will(returnValue(emptyList()));
oneOf(plugin).poll(connected); // Poll the plugin
oneOf(plugin).poll(singletonMap(contactId, properties));
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId)); p.eventOccurred(new TransportEnabledEvent(transportId));
} }
@Test @Test
public void testCancelsPollingOnTransportDisabled() throws Exception { public void testDoesNotPollIfAllContactsAreConnected() throws Exception {
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
context.checking(new Expectations() {{
allowing(plugin).getId();
will(returnValue(transportId));
// Get the plugin
oneOf(pluginManager).getPlugin(transportId);
will(returnValue(plugin));
// The plugin supports polling
oneOf(plugin).shouldPoll();
will(returnValue(true));
// Schedule a polling task immediately
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)), with(0L),
with(MILLISECONDS));
will(returnValue(future));
will(new RunAction());
// Running the polling task schedules the next polling task
oneOf(plugin).getPollingInterval();
will(returnValue(pollingInterval));
oneOf(random).nextDouble();
will(returnValue(0.5));
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(scheduler).schedule(with(any(Runnable.class)),
with((long) (pollingInterval * 0.5)), with(MILLISECONDS));
will(returnValue(future));
// Get the transport properties and connected contacts
oneOf(transportPropertyManager).getRemoteProperties(transportId);
will(returnValue(singletonMap(contactId, properties)));
oneOf(connectionRegistry).getConnectedContacts(transportId);
will(returnValue(singletonList(contactId)));
// All contacts are connected, so don't poll the plugin
}});
Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId));
}
@Test
public void testCancelsPollingOnTransportDisabled() {
Plugin plugin = context.mock(Plugin.class); Plugin plugin = context.mock(Plugin.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -371,7 +442,8 @@ public class PollerTest extends BrambleMockTestCase {
}}); }});
Poller p = new Poller(ioExecutor, scheduler, connectionManager, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId)); p.eventOccurred(new TransportEnabledEvent(transportId));
p.eventOccurred(new TransportDisabledEvent(transportId)); p.eventOccurred(new TransportDisabledEvent(transportId));

View File

@@ -23,8 +23,6 @@ import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@@ -40,7 +38,6 @@ import static org.junit.Assert.assertTrue;
public class LanTcpPluginTest extends BrambleTestCase { public class LanTcpPluginTest extends BrambleTestCase {
private final ContactId contactId = new ContactId(234);
private final Backoff backoff = new TestBackoff(); private final Backoff backoff = new TestBackoff();
@Test @Test
@@ -160,12 +157,10 @@ public class LanTcpPluginTest extends BrambleTestCase {
error.set(true); error.set(true);
} }
}).start(); }).start();
// Tell the plugin about the port // Connect to the port
TransportProperties p = new TransportProperties(); TransportProperties p = new TransportProperties();
p.put("ipPorts", addrString + ":" + port); p.put("ipPorts", addrString + ":" + port);
callback.remote.put(contactId, p); DuplexTransportConnection d = plugin.createConnection(p);
// Connect to the port
DuplexTransportConnection d = plugin.createConnection(contactId);
assertNotNull(d); assertNotNull(d);
// Check that the connection was accepted // Check that the connection was accepted
assertTrue(latch.await(5, SECONDS)); assertTrue(latch.await(5, SECONDS));
@@ -281,7 +276,7 @@ public class LanTcpPluginTest extends BrambleTestCase {
} }
@Test @Test
public void testComparatorPrefersNonZeroPorts() throws Exception { public void testComparatorPrefersNonZeroPorts() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator(); Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress nonZero = new InetSocketAddress("1.2.3.4", 1234); InetSocketAddress nonZero = new InetSocketAddress("1.2.3.4", 1234);
InetSocketAddress zero = new InetSocketAddress("1.2.3.4", 0); InetSocketAddress zero = new InetSocketAddress("1.2.3.4", 0);
@@ -294,7 +289,7 @@ public class LanTcpPluginTest extends BrambleTestCase {
} }
@Test @Test
public void testComparatorPrefersLongerPrefixes() throws Exception { public void testComparatorPrefersLongerPrefixes() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator(); Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0); InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0);
InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0); InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0);
@@ -314,7 +309,7 @@ public class LanTcpPluginTest extends BrambleTestCase {
} }
@Test @Test
public void testComparatorPrefersSiteLocalToLinkLocal() throws Exception { public void testComparatorPrefersSiteLocalToLinkLocal() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator(); Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0); InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0);
InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0); InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0);
@@ -345,8 +340,6 @@ public class LanTcpPluginTest extends BrambleTestCase {
@NotNullByDefault @NotNullByDefault
private static class Callback implements DuplexPluginCallback { private static class Callback implements DuplexPluginCallback {
private final Map<ContactId, TransportProperties> remote =
new Hashtable<>();
private final CountDownLatch propertiesLatch = new CountDownLatch(1); private final CountDownLatch propertiesLatch = new CountDownLatch(1);
private final CountDownLatch connectionsLatch = new CountDownLatch(1); private final CountDownLatch connectionsLatch = new CountDownLatch(1);
private final TransportProperties local = new TransportProperties(); private final TransportProperties local = new TransportProperties();
@@ -361,16 +354,6 @@ public class LanTcpPluginTest extends BrambleTestCase {
return local; return local;
} }
@Override
public Map<ContactId, TransportProperties> getRemoteProperties() {
return remote;
}
@Override
public TransportProperties getRemoteProperties(ContactId c) {
return remote.get(c);
}
@Override @Override
public void mergeSettings(Settings s) { public void mergeSettings(Settings s) {
} }
@@ -381,20 +364,6 @@ public class LanTcpPluginTest extends BrambleTestCase {
propertiesLatch.countDown(); propertiesLatch.countDown();
} }
@Override
public int showChoice(String[] options, String... message) {
return -1;
}
@Override
public boolean showConfirmationMessage(String... message) {
return false;
}
@Override
public void showMessage(String... message) {
}
@Override @Override
public void incomingConnectionCreated(DuplexTransportConnection d) { public void incomingConnectionCreated(DuplexTransportConnection d) {
connectionsLatch.countDown(); connectionsLatch.countDown();

View File

@@ -7,11 +7,10 @@ import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.SyncRecordWriter; import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor; import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.TestUtils;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test; import org.junit.Test;
import java.util.Arrays; import java.util.Arrays;
@@ -19,33 +18,27 @@ import java.util.Collections;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
public class SimplexOutgoingSessionTest extends BrambleTestCase { public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
private final Mockery context; private static final int MAX_LATENCY = Integer.MAX_VALUE;
private final DatabaseComponent db;
private final Executor dbExecutor;
private final EventBus eventBus;
private final ContactId contactId;
private final MessageId messageId;
private final int maxLatency;
private final SyncRecordWriter recordWriter;
public SimplexOutgoingSessionTest() { private final DatabaseComponent db = context.mock(DatabaseComponent.class);
context = new Mockery(); private final EventBus eventBus = context.mock(EventBus.class);
db = context.mock(DatabaseComponent.class); private final StreamWriter streamWriter = context.mock(StreamWriter.class);
dbExecutor = new ImmediateExecutor(); private final SyncRecordWriter recordWriter =
eventBus = context.mock(EventBus.class); context.mock(SyncRecordWriter.class);
recordWriter = context.mock(SyncRecordWriter.class);
contactId = new ContactId(234); private final Executor dbExecutor = new ImmediateExecutor();
messageId = new MessageId(TestUtils.getRandomId()); private final ContactId contactId = new ContactId(234);
maxLatency = Integer.MAX_VALUE; private final MessageId messageId = new MessageId(getRandomId());
}
@Test @Test
public void testNothingToSend() throws Exception { public void testNothingToSend() throws Exception {
SimplexOutgoingSession session = new SimplexOutgoingSession(db, SimplexOutgoingSession session = new SimplexOutgoingSession(db,
dbExecutor, eventBus, contactId, maxLatency, recordWriter); dbExecutor, eventBus, contactId, MAX_LATENCY, streamWriter,
recordWriter);
Transaction noAckTxn = new Transaction(null, false); Transaction noAckTxn = new Transaction(null, false);
Transaction noMsgTxn = new Transaction(null, false); Transaction noMsgTxn = new Transaction(null, false);
@@ -63,19 +56,17 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
oneOf(db).startTransaction(false); oneOf(db).startTransaction(false);
will(returnValue(noMsgTxn)); will(returnValue(noMsgTxn));
oneOf(db).generateBatch(with(noMsgTxn), with(contactId), oneOf(db).generateBatch(with(noMsgTxn), with(contactId),
with(any(int.class)), with(maxLatency)); with(any(int.class)), with(MAX_LATENCY));
will(returnValue(null)); will(returnValue(null));
oneOf(db).commitTransaction(noMsgTxn); oneOf(db).commitTransaction(noMsgTxn);
oneOf(db).endTransaction(noMsgTxn); oneOf(db).endTransaction(noMsgTxn);
// Flush the output stream // Send the end of stream marker
oneOf(recordWriter).flush(); oneOf(streamWriter).sendEndOfStream();
// Remove listener // Remove listener
oneOf(eventBus).removeListener(session); oneOf(eventBus).removeListener(session);
}}); }});
session.run(); session.run();
context.assertIsSatisfied();
} }
@Test @Test
@@ -83,7 +74,8 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
Ack ack = new Ack(Collections.singletonList(messageId)); Ack ack = new Ack(Collections.singletonList(messageId));
byte[] raw = new byte[1234]; byte[] raw = new byte[1234];
SimplexOutgoingSession session = new SimplexOutgoingSession(db, SimplexOutgoingSession session = new SimplexOutgoingSession(db,
dbExecutor, eventBus, contactId, maxLatency, recordWriter); dbExecutor, eventBus, contactId, MAX_LATENCY, streamWriter,
recordWriter);
Transaction ackTxn = new Transaction(null, false); Transaction ackTxn = new Transaction(null, false);
Transaction noAckTxn = new Transaction(null, false); Transaction noAckTxn = new Transaction(null, false);
Transaction msgTxn = new Transaction(null, false); Transaction msgTxn = new Transaction(null, false);
@@ -104,7 +96,7 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
oneOf(db).startTransaction(false); oneOf(db).startTransaction(false);
will(returnValue(msgTxn)); will(returnValue(msgTxn));
oneOf(db).generateBatch(with(msgTxn), with(contactId), oneOf(db).generateBatch(with(msgTxn), with(contactId),
with(any(int.class)), with(maxLatency)); with(any(int.class)), with(MAX_LATENCY));
will(returnValue(Arrays.asList(raw))); will(returnValue(Arrays.asList(raw)));
oneOf(db).commitTransaction(msgTxn); oneOf(db).commitTransaction(msgTxn);
oneOf(db).endTransaction(msgTxn); oneOf(db).endTransaction(msgTxn);
@@ -120,18 +112,16 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
oneOf(db).startTransaction(false); oneOf(db).startTransaction(false);
will(returnValue(noMsgTxn)); will(returnValue(noMsgTxn));
oneOf(db).generateBatch(with(noMsgTxn), with(contactId), oneOf(db).generateBatch(with(noMsgTxn), with(contactId),
with(any(int.class)), with(maxLatency)); with(any(int.class)), with(MAX_LATENCY));
will(returnValue(null)); will(returnValue(null));
oneOf(db).commitTransaction(noMsgTxn); oneOf(db).commitTransaction(noMsgTxn);
oneOf(db).endTransaction(noMsgTxn); oneOf(db).endTransaction(noMsgTxn);
// Flush the output stream // Send the end of stream marker
oneOf(recordWriter).flush(); oneOf(streamWriter).sendEndOfStream();
// Remove listener // Remove listener
oneOf(eventBus).removeListener(session); oneOf(eventBus).removeListener(session);
}}); }});
session.run(); session.run();
context.assertIsSatisfied();
} }
} }

View File

@@ -19,6 +19,7 @@ import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.SyncRecordWriterFactory; import org.briarproject.bramble.api.sync.SyncRecordWriterFactory;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory; import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory; import org.briarproject.bramble.api.transport.StreamWriterFactory;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
@@ -27,7 +28,6 @@ import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@@ -102,18 +102,18 @@ public class SyncIntegrationTest extends BrambleTestCase {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamContext ctx = new StreamContext(contactId, transportId, tagKey, StreamContext ctx = new StreamContext(contactId, transportId, tagKey,
headerKey, streamNumber); headerKey, streamNumber);
OutputStream streamWriter = streamWriterFactory.createStreamWriter(out, StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out,
ctx); ctx);
SyncRecordWriter recordWriter = recordWriterFactory.createRecordWriter( SyncRecordWriter recordWriter = recordWriterFactory.createRecordWriter(
streamWriter); streamWriter.getOutputStream());
recordWriter.writeAck(new Ack(messageIds)); recordWriter.writeAck(new Ack(messageIds));
recordWriter.writeMessage(message.getRaw()); recordWriter.writeMessage(message.getRaw());
recordWriter.writeMessage(message1.getRaw()); recordWriter.writeMessage(message1.getRaw());
recordWriter.writeOffer(new Offer(messageIds)); recordWriter.writeOffer(new Offer(messageIds));
recordWriter.writeRequest(new Request(messageIds)); recordWriter.writeRequest(new Request(messageIds));
recordWriter.flush();
streamWriter.sendEndOfStream();
return out.toByteArray(); return out.toByteArray();
} }

View File

@@ -20,7 +20,7 @@ public class CaptureArgumentAction<T> implements Action {
} }
@Override @Override
public Object invoke(Invocation invocation) throws Throwable { public Object invoke(Invocation invocation) {
captured.set(capturedClass.cast(invocation.getParameter(index))); captured.set(capturedClass.cast(invocation.getParameter(index)));
return null; return null;
} }

View File

@@ -9,25 +9,31 @@ import java.io.File;
@NotNullByDefault @NotNullByDefault
public class TestDatabaseConfig implements DatabaseConfig { public class TestDatabaseConfig implements DatabaseConfig {
private final File dir; private final File dbDir, keyDir;
private final long maxSize; private final long maxSize;
private volatile SecretKey key = new SecretKey(new byte[SecretKey.LENGTH]); private volatile SecretKey key = new SecretKey(new byte[SecretKey.LENGTH]);
public TestDatabaseConfig(File dir, long maxSize) { public TestDatabaseConfig(File testDir, long maxSize) {
this.dir = dir; dbDir = new File(testDir, "db");
keyDir = new File(testDir, "key");
this.maxSize = maxSize; this.maxSize = maxSize;
} }
@Override @Override
public boolean databaseExists() { public boolean databaseExists() {
if (!dir.isDirectory()) return false; if (!dbDir.isDirectory()) return false;
File[] files = dir.listFiles(); File[] files = dbDir.listFiles();
return files != null && files.length > 0; return files != null && files.length > 0;
} }
@Override @Override
public File getDatabaseDirectory() { public File getDatabaseDirectory() {
return dir; return dbDir;
}
@Override
public File getDatabaseKeyDirectory() {
return keyDir;
} }
@Override @Override

View File

@@ -9,13 +9,14 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@Module @Module
@@ -51,12 +52,17 @@ public class TestPluginConfigModule {
@Override @Override
public Collection<DuplexPluginFactory> getDuplexFactories() { public Collection<DuplexPluginFactory> getDuplexFactories() {
return Collections.emptyList(); return emptyList();
} }
@Override @Override
public Collection<SimplexPluginFactory> getSimplexFactories() { public Collection<SimplexPluginFactory> getSimplexFactories() {
return Collections.singletonList(simplex); return singletonList(simplex);
}
@Override
public boolean shouldPoll() {
return false;
} }
}; };
return pluginConfig; return pluginConfig;

View File

@@ -22,6 +22,7 @@ import org.junit.Test;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import java.util.Random; import java.util.Random;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
@@ -54,6 +55,7 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
new StreamContext(contactId, transportId, getSecretKey(), new StreamContext(contactId, transportId, getSecretKey(),
getSecretKey(), 1); getSecretKey(), 1);
private final byte[] tag = getRandomBytes(TAG_LENGTH); private final byte[] tag = getRandomBytes(TAG_LENGTH);
private final Random random = new Random();
private final KeyManagerImpl keyManager = new KeyManagerImpl(db, executor, private final KeyManagerImpl keyManager = new KeyManagerImpl(db, executor,
pluginConfig, transportKeyManagerFactory); pluginConfig, transportKeyManagerFactory);
@@ -102,30 +104,18 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
public void testAddContact() throws Exception { public void testAddContact() throws Exception {
SecretKey secretKey = getSecretKey(); SecretKey secretKey = getSecretKey();
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();
boolean alice = new Random().nextBoolean(); boolean alice = random.nextBoolean();
boolean active = random.nextBoolean();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportKeyManager).addContact(txn, contactId, secretKey, oneOf(transportKeyManager).addContact(txn, contactId, secretKey,
timestamp, alice); timestamp, alice, active);
}});
keyManager.addContact(txn, contactId, secretKey, timestamp, alice);
}
@Test
public void testAddUnboundKeys() throws Exception {
SecretKey secretKey = getSecretKey();
long timestamp = System.currentTimeMillis();
boolean alice = new Random().nextBoolean();
context.checking(new Expectations() {{
oneOf(transportKeyManager).addUnboundKeys(txn, secretKey,
timestamp, alice);
will(returnValue(keySetId)); will(returnValue(keySetId));
}}); }});
assertEquals(singletonMap(transportId, keySetId), Map<TransportId, KeySetId> ids = keyManager.addContact(txn, contactId,
keyManager.addUnboundKeys(txn, secretKey, timestamp, alice)); secretKey, timestamp, alice, active);
assertEquals(singletonMap(transportId, keySetId), ids);
} }
@Test @Test

View File

@@ -61,7 +61,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
private final ContactId contactId1 = new ContactId(234); private final ContactId contactId1 = new ContactId(234);
private final KeySetId keySetId = new KeySetId(345); private final KeySetId keySetId = new KeySetId(345);
private final KeySetId keySetId1 = new KeySetId(456); private final KeySetId keySetId1 = new KeySetId(456);
private final KeySetId keySetId2 = new KeySetId(567);
private final SecretKey tagKey = TestUtils.getSecretKey(); private final SecretKey tagKey = TestUtils.getSecretKey();
private final SecretKey headerKey = TestUtils.getSecretKey(); private final SecretKey headerKey = TestUtils.getSecretKey();
private final SecretKey masterKey = TestUtils.getSecretKey(); private final SecretKey masterKey = TestUtils.getSecretKey();
@@ -71,14 +70,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
public void testKeysAreRotatedAtStartup() throws Exception { public void testKeysAreRotatedAtStartup() throws Exception {
TransportKeys shouldRotate = createTransportKeys(900, 0, true); TransportKeys shouldRotate = createTransportKeys(900, 0, true);
TransportKeys shouldNotRotate = createTransportKeys(1000, 0, true); TransportKeys shouldNotRotate = createTransportKeys(1000, 0, true);
TransportKeys shouldRotate1 = createTransportKeys(999, 0, false);
Collection<KeySet> loaded = asList( Collection<KeySet> loaded = asList(
new KeySet(keySetId, contactId, shouldRotate), new KeySet(keySetId, contactId, shouldRotate),
new KeySet(keySetId1, contactId1, shouldNotRotate), new KeySet(keySetId1, contactId1, shouldNotRotate)
new KeySet(keySetId2, null, shouldRotate1)
); );
TransportKeys rotated = createTransportKeys(1000, 0, true); TransportKeys rotated = createTransportKeys(1000, 0, true);
TransportKeys rotated1 = createTransportKeys(1000, 0, false);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -93,8 +89,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
will(returnValue(rotated)); will(returnValue(rotated));
oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000); oneOf(transportCrypto).rotateTransportKeys(shouldNotRotate, 1000);
will(returnValue(shouldNotRotate)); will(returnValue(shouldNotRotate));
oneOf(transportCrypto).rotateTransportKeys(shouldRotate1, 1000);
will(returnValue(rotated1));
// Encode the tags (3 sets per contact) // Encode the tags (3 sets per contact)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(6).of(transportCrypto).encodeTag( exactly(6).of(transportCrypto).encodeTag(
@@ -103,10 +97,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were rotated // Save the keys that were rotated
oneOf(db).updateTransportKeys(txn, asList( oneOf(db).updateTransportKeys(txn,
new KeySet(keySetId, contactId, rotated), singletonList(new KeySet(keySetId, contactId, rotated)));
new KeySet(keySetId2, null, rotated1))
);
// Schedule key rotation at the start of the next rotation period // Schedule key rotation at the start of the next rotation period
oneOf(scheduler).schedule(with(any(Runnable.class)), oneOf(scheduler).schedule(with(any(Runnable.class)),
with(rotationPeriodLength - 1), with(MILLISECONDS)); with(rotationPeriodLength - 1), with(MILLISECONDS));
@@ -153,43 +145,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency); maxLatency);
// The timestamp is 1 ms before the start of rotation period 1000 // The timestamp is 1 ms before the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000 - 1; long timestamp = rotationPeriodLength * 1000 - 1;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
alice); masterKey, timestamp, alice, true));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
} }
@Test
public void testKeysAreRotatedWhenAddingUnboundKeys() throws Exception {
boolean alice = random.nextBoolean();
TransportKeys transportKeys = createTransportKeys(999, 0, false);
TransportKeys rotated = createTransportKeys(1000, 0, false);
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
999, alice, false);
will(returnValue(transportKeys));
// Get the current time (1 ms after start of rotation period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000 + 1));
// Rotate the transport keys
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(rotated));
// Save the keys
oneOf(db).addTransportKeys(txn, null, rotated);
will(returnValue(keySetId));
}});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is 1 ms before the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000 - 1;
assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
masterKey, timestamp, alice));
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
}
@Test @Test
public void testOutgoingStreamContextIsNullIfContactIsNotFound() public void testOutgoingStreamContextIsNullIfContactIsNotFound()
throws Exception { throws Exception {
@@ -211,15 +171,15 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
MAX_32_BIT_UNSIGNED + 1, true); MAX_32_BIT_UNSIGNED + 1, true);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
expectAddContactNoRotation(alice, transportKeys, txn); expectAddContactNoRotation(alice, true, transportKeys, txn);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
alice); masterKey, timestamp, alice, true));
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
} }
@@ -232,7 +192,7 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
MAX_32_BIT_UNSIGNED, true); MAX_32_BIT_UNSIGNED, true);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
expectAddContactNoRotation(alice, transportKeys, txn); expectAddContactNoRotation(alice, true, transportKeys, txn);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Increment the stream counter // Increment the stream counter
@@ -244,8 +204,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
alice); masterKey, timestamp, alice, true));
// The first request should return a stream context // The first request should return a stream context
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
StreamContext ctx = transportKeyManager.getStreamContext(txn, StreamContext ctx = transportKeyManager.getStreamContext(txn,
@@ -265,19 +225,21 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
public void testIncomingStreamContextIsNullIfTagIsNotFound() public void testIncomingStreamContextIsNullIfTagIsNotFound()
throws Exception { throws Exception {
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
TransportKeys transportKeys = createTransportKeys(1000, 0, true); boolean active = random.nextBoolean();
TransportKeys transportKeys = createTransportKeys(1000, 0, active);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
expectAddContactNoRotation(alice, transportKeys, txn); expectAddContactNoRotation(alice, active, transportKeys, txn);
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl( TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId, db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
alice); masterKey, timestamp, alice, active));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertEquals(active,
transportKeyManager.canSendOutgoingStreams(contactId));
// The tag should not be recognised // The tag should not be recognised
assertNull(transportKeyManager.getStreamContext(txn, assertNull(transportKeyManager.getStreamContext(txn,
new byte[TAG_LENGTH])); new byte[TAG_LENGTH]));
@@ -327,8 +289,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
transportKeyManager.addContact(txn, contactId, masterKey, timestamp, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
alice); masterKey, timestamp, alice, true));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId)); assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
// Use the first tag (previous rotation period, stream number 0) // Use the first tag (previous rotation period, stream number 0)
assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size()); assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
@@ -415,23 +377,14 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testBindingAndActivatingKeys() throws Exception { public void testActivatingKeys() throws Exception {
boolean alice = random.nextBoolean(); boolean alice = random.nextBoolean();
TransportKeys transportKeys = createTransportKeys(1000, 0, false); TransportKeys transportKeys = createTransportKeys(1000, 0, false);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
expectAddUnboundKeysNoRotation(alice, transportKeys, txn); expectAddContactNoRotation(alice, false, transportKeys, txn);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// When the keys are bound, encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction());
}
// Save the key binding
oneOf(db).bindTransportKeys(txn, contactId, transportId, keySetId);
// Activate the keys // Activate the keys
oneOf(db).setTransportKeysActive(txn, transportId, keySetId); oneOf(db).setTransportKeysActive(txn, transportId, keySetId);
// Increment the stream counter // Increment the stream counter
@@ -443,12 +396,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
masterKey, timestamp, alice)); masterKey, timestamp, alice, false));
// The keys are unbound so no stream context should be returned
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId));
transportKeyManager.bindKeys(txn, contactId, keySetId);
// The keys are inactive so no stream context should be returned // The keys are inactive so no stream context should be returned
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
@@ -474,18 +423,26 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
// Keep a copy of the tags // Keep a copy of the tags
List<byte[]> tags = new ArrayList<>(); List<byte[]> tags = new ArrayList<>();
expectAddUnboundKeysNoRotation(alice, transportKeys, txn);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// When the keys are bound, encode the tags (3 sets) oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
1000, alice, false);
will(returnValue(transportKeys));
// Get the current time (the start of rotation period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000));
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag( exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey), with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i)); with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction(tags)); will(new EncodeTagAction(tags));
} }
// Save the key binding // Rotate the transport keys (the keys are unaffected)
oneOf(db).bindTransportKeys(txn, contactId, transportId, keySetId); oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys));
// Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
will(returnValue(keySetId));
// Encode a new tag after sliding the window // Encode a new tag after sliding the window
oneOf(transportCrypto).encodeTag(with(any(byte[].class)), oneOf(transportCrypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(tagKey), with(PROTOCOL_VERSION),
@@ -505,9 +462,8 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
maxLatency); maxLatency);
// The timestamp is at the start of rotation period 1000 // The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000; long timestamp = rotationPeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn, assertEquals(keySetId, transportKeyManager.addContact(txn, contactId,
masterKey, timestamp, alice)); masterKey, timestamp, alice, false));
transportKeyManager.bindKeys(txn, contactId, keySetId);
// The keys are inactive so no stream context should be returned // The keys are inactive so no stream context should be returned
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId)); assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
assertNull(transportKeyManager.getStreamContext(txn, contactId)); assertNull(transportKeyManager.getStreamContext(txn, contactId));
@@ -532,36 +488,11 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
assertEquals(0L, ctx.getStreamNumber()); assertEquals(0L, ctx.getStreamNumber());
} }
@Test private void expectAddContactNoRotation(boolean alice, boolean active,
public void testRemovingUnboundKeys() throws Exception {
boolean alice = random.nextBoolean();
TransportKeys transportKeys = createTransportKeys(1000, 0, false);
Transaction txn = new Transaction(null, false);
expectAddUnboundKeysNoRotation(alice, transportKeys, txn);
context.checking(new Expectations() {{
// Remove the unbound keys
oneOf(db).removeTransportKeys(txn, transportId, keySetId);
}});
TransportKeyManager transportKeyManager = new TransportKeyManagerImpl(
db, transportCrypto, dbExecutor, scheduler, clock, transportId,
maxLatency);
// The timestamp is at the start of rotation period 1000
long timestamp = rotationPeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addUnboundKeys(txn,
masterKey, timestamp, alice));
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
transportKeyManager.removeKeys(txn, keySetId);
assertFalse(transportKeyManager.canSendOutgoingStreams(contactId));
}
private void expectAddContactNoRotation(boolean alice,
TransportKeys transportKeys, Transaction txn) throws Exception { TransportKeys transportKeys, Transaction txn) throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey, oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
1000, alice, true); 1000, alice, active);
will(returnValue(transportKeys)); will(returnValue(transportKeys));
// Get the current time (the start of rotation period 1000) // Get the current time (the start of rotation period 1000)
oneOf(clock).currentTimeMillis(); oneOf(clock).currentTimeMillis();
@@ -582,24 +513,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
}}); }});
} }
private void expectAddUnboundKeysNoRotation(boolean alice,
TransportKeys transportKeys, Transaction txn) throws Exception {
context.checking(new Expectations() {{
oneOf(transportCrypto).deriveTransportKeys(transportId, masterKey,
1000, alice, false);
will(returnValue(transportKeys));
// Get the current time (the start of rotation period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(rotationPeriodLength * 1000));
// Rotate the transport keys (the keys are unaffected)
oneOf(transportCrypto).rotateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys));
// Save the unbound keys
oneOf(db).addTransportKeys(txn, null, transportKeys);
will(returnValue(keySetId));
}});
}
private TransportKeys createTransportKeys(long rotationPeriod, private TransportKeys createTransportKeys(long rotationPeriod,
long streamCounter, boolean active) { long streamCounter, boolean active) {
IncomingKeys inPrev = new IncomingKeys(tagKey, headerKey, IncomingKeys inPrev = new IncomingKeys(tagKey, headerKey,

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -10,20 +10,20 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory; import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory;
import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory; import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory;
import org.briarproject.bramble.plugin.file.RemovableDrivePluginFactory;
import org.briarproject.bramble.plugin.modem.ModemPluginFactory; import org.briarproject.bramble.plugin.modem.ModemPluginFactory;
import org.briarproject.bramble.plugin.tcp.LanTcpPluginFactory; import org.briarproject.bramble.plugin.tcp.LanTcpPluginFactory;
import org.briarproject.bramble.plugin.tcp.WanTcpPluginFactory; import org.briarproject.bramble.plugin.tcp.WanTcpPluginFactory;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
@Module @Module
public class DesktopPluginModule extends PluginModule { public class DesktopPluginModule extends PluginModule {
@@ -41,12 +41,8 @@ public class DesktopPluginModule extends PluginModule {
backoffFactory); backoffFactory);
DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor, DuplexPluginFactory wan = new WanTcpPluginFactory(ioExecutor,
backoffFactory, shutdownManager); backoffFactory, shutdownManager);
SimplexPluginFactory removable =
new RemovableDrivePluginFactory(ioExecutor);
Collection<SimplexPluginFactory> simplex =
Collections.singletonList(removable);
Collection<DuplexPluginFactory> duplex = Collection<DuplexPluginFactory> duplex =
Arrays.asList(bluetooth, modem, lan, wan); asList(bluetooth, modem, lan, wan);
@NotNullByDefault @NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() { PluginConfig pluginConfig = new PluginConfig() {
@@ -57,7 +53,12 @@ public class DesktopPluginModule extends PluginModule {
@Override @Override
public Collection<SimplexPluginFactory> getSimplexFactories() { public Collection<SimplexPluginFactory> getSimplexFactories() {
return simplex; return emptyList();
}
@Override
public boolean shouldPoll() {
return true;
} }
}; };
return pluginConfig; return pluginConfig;

View File

@@ -1,28 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
@NotNullByDefault
class LinuxRemovableDriveFinder extends UnixRemovableDriveFinder {
@Override
protected String getMountCommand() {
return "/bin/mount";
}
@Override
@Nullable
protected String parseMountPoint(String line) {
// The format is "/dev/foo on /bar/baz type bam (opt1,opt2)"
String pattern = "^/dev/[^ ]+ on (.*) type [^ ]+ \\([^)]+\\)$";
String path = line.replaceFirst(pattern, "$1");
return path.equals(line) ? null : path;
}
@Override
protected boolean isRemovableDriveMountPoint(String path) {
return path.startsWith("/mnt/") || path.startsWith("/media/");
}
}

View File

@@ -1,12 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class LinuxRemovableDriveMonitor extends UnixRemovableDriveMonitor {
@Override
protected String[] getPathsToWatch() {
return new String[] {"/mnt", "/media"};
}
}

View File

@@ -1,28 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
@NotNullByDefault
class MacRemovableDriveFinder extends UnixRemovableDriveFinder {
@Override
protected String getMountCommand() {
return "/sbin/mount";
}
@Override
@Nullable
protected String parseMountPoint(String line) {
// The format is "/dev/foo on /bar/baz (opt1, opt2)"
String pattern = "^/dev/[^ ]+ on (.*) \\([^)]+\\)$";
String path = line.replaceFirst(pattern, "$1");
return path.equals(line) ? null : path;
}
@Override
protected boolean isRemovableDriveMountPoint(String path) {
return path.startsWith("/Volumes/");
}
}

View File

@@ -1,12 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class MacRemovableDriveMonitor extends UnixRemovableDriveMonitor {
@Override
protected String[] getPathsToWatch() {
return new String[] {"/Volumes"};
}
}

View File

@@ -1,84 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
private static final Logger LOG =
Logger.getLogger(PollingRemovableDriveMonitor.class.getName());
private final Executor ioExecutor;
private final RemovableDriveFinder finder;
private final int pollingInterval;
private final Lock pollingLock = new ReentrantLock();
private final Condition stopPolling = pollingLock.newCondition();
private volatile boolean running = false;
private volatile Callback callback = null;
PollingRemovableDriveMonitor(Executor ioExecutor,
RemovableDriveFinder finder, int pollingInterval) {
this.ioExecutor = ioExecutor;
this.finder = finder;
this.pollingInterval = pollingInterval;
}
@Override
public void start(Callback callback) throws IOException {
this.callback = callback;
running = true;
ioExecutor.execute(this);
}
@Override
public void stop() throws IOException {
running = false;
pollingLock.lock();
try {
stopPolling.signalAll();
} finally {
pollingLock.unlock();
}
}
@Override
public void run() {
try {
Collection<File> drives = finder.findRemovableDrives();
while (running) {
pollingLock.lock();
try {
stopPolling.await(pollingInterval, MILLISECONDS);
} finally {
pollingLock.unlock();
}
if (!running) return;
Collection<File> newDrives = finder.findRemovableDrives();
for (File f : newDrives) {
if (!drives.contains(f)) callback.driveInserted(f);
}
drives = newDrives;
}
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting to poll");
Thread.currentThread().interrupt();
} catch (IOException e) {
callback.exceptionThrown(e);
}
}
}

View File

@@ -1,13 +0,0 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
@NotNullByDefault
interface RemovableDriveFinder {
Collection<File> findRemovableDrives() throws IOException;
}

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