mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 22:29:53 +01:00
Compare commits
26 Commits
1528-log-t
...
poll-own-h
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66d907d1b2 | ||
|
|
7d336c98e4 | ||
|
|
963e510c3b | ||
|
|
213d2f1da4 | ||
|
|
3038b92dbc | ||
|
|
7cd3c2890b | ||
|
|
a38933df66 | ||
|
|
4993873ae2 | ||
|
|
02b805ce42 | ||
|
|
1a6ba16a59 | ||
|
|
654a05df8a | ||
|
|
ffe1876337 | ||
|
|
98963955b1 | ||
|
|
d83efce002 | ||
|
|
efb1b8c1ad | ||
|
|
3f36db8b3a | ||
|
|
a2f4e70a48 | ||
|
|
01e72eff40 | ||
|
|
6288577daa | ||
|
|
5d363496bd | ||
|
|
713be403eb | ||
|
|
2fd948b81d | ||
|
|
97d11cc602 | ||
|
|
79f41064e4 | ||
|
|
9aacd9d3d8 | ||
|
|
2b4a1cf54b |
@@ -86,8 +86,8 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
|||||||
BluetoothConnectionFactory<BluetoothSocket> connectionFactory =
|
BluetoothConnectionFactory<BluetoothSocket> connectionFactory =
|
||||||
new AndroidBluetoothConnectionFactory(connectionLimiter,
|
new AndroidBluetoothConnectionFactory(connectionLimiter,
|
||||||
wakeLockManager, timeoutMonitor);
|
wakeLockManager, timeoutMonitor);
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(eventBus, ID,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
||||||
connectionLimiter, connectionFactory, ioExecutor,
|
connectionLimiter, connectionFactory, ioExecutor,
|
||||||
wakefulIoExecutor, secureRandom, androidExecutor, app,
|
wakefulIoExecutor, secureRandom, androidExecutor, app,
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexPlugin createPlugin(PluginCallback callback) {
|
public DuplexPlugin createPlugin(PluginCallback callback) {
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(eventBus, ID,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor,
|
AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor,
|
||||||
wakefulIoExecutor, app, backoff, callback,
|
wakefulIoExecutor, app, backoff, callback,
|
||||||
MAX_LATENCY, MAX_IDLE_TIME, CONNECTION_TIMEOUT);
|
MAX_LATENCY, MAX_IDLE_TIME, CONNECTION_TIMEOUT);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.battery.BatteryManager;
|
|||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||||
@@ -64,7 +63,6 @@ class AndroidTorPlugin extends TorPlugin {
|
|||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
AndroidWakeLockManager wakeLockManager,
|
AndroidWakeLockManager wakeLockManager,
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
TorRendezvousCrypto torRendezvousCrypto,
|
||||||
PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture,
|
String architecture,
|
||||||
@@ -75,7 +73,7 @@ class AndroidTorPlugin extends TorPlugin {
|
|||||||
int torControlPort) {
|
int torControlPort) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
torSocketFactory, clock, resourceProvider,
|
torSocketFactory, clock, resourceProvider,
|
||||||
circumventionProvider, batteryManager, backoff,
|
circumventionProvider, batteryManager,
|
||||||
torRendezvousCrypto, callback, architecture, maxLatency,
|
torRendezvousCrypto, callback, architecture, maxLatency,
|
||||||
maxIdleTime, torDirectory, torSocksPort, torControlPort);
|
maxIdleTime, torDirectory, torSocksPort, torControlPort);
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
@@ -44,7 +42,6 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
|||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory,
|
|
||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
@@ -56,7 +53,7 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
|||||||
Application app,
|
Application app,
|
||||||
AndroidWakeLockManager wakeLockManager) {
|
AndroidWakeLockManager wakeLockManager) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
eventBus, torSocketFactory, backoffFactory, resourceProvider,
|
eventBus, torSocketFactory, resourceProvider,
|
||||||
circumventionProvider, batteryManager, clock, crypto,
|
circumventionProvider, batteryManager, clock, crypto,
|
||||||
torDirectory, torSocksPort, torControlPort);
|
torDirectory, torSocksPort, torControlPort);
|
||||||
this.app = app;
|
this.app = app;
|
||||||
@@ -76,14 +73,14 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
TorPlugin createPluginInstance(Backoff backoff,
|
TorPlugin createPluginInstance(TorRendezvousCrypto torRendezvousCrypto,
|
||||||
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture) {
|
String architecture) {
|
||||||
return new AndroidTorPlugin(ioExecutor,
|
return new AndroidTorPlugin(ioExecutor,
|
||||||
wakefulIoExecutor, app, networkManager, locationUtils,
|
wakefulIoExecutor, app, networkManager, locationUtils,
|
||||||
torSocketFactory, clock, resourceProvider,
|
torSocketFactory, clock, resourceProvider,
|
||||||
circumventionProvider, batteryManager, wakeLockManager,
|
circumventionProvider, batteryManager, wakeLockManager,
|
||||||
backoff, torRendezvousCrypto, callback, architecture,
|
torRendezvousCrypto, callback, architecture,
|
||||||
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
||||||
torControlPort);
|
torControlPort);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package org.briarproject.bramble.api.plugin;
|
package org.briarproject.bramble.api.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
|
||||||
public interface BackoffFactory {
|
public interface BackoffFactory {
|
||||||
|
|
||||||
Backoff createBackoff(int minInterval, int maxInterval,
|
Backoff createBackoff(EventBus eventBus, TransportId transportId,
|
||||||
double base);
|
int minInterval, int maxInterval, double base);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,4 +56,9 @@ public interface PluginCallback extends ConnectionHandler {
|
|||||||
* This method can safely be called while holding a lock.
|
* This method can safely be called while holding a lock.
|
||||||
*/
|
*/
|
||||||
void pluginStateChanged(State state);
|
void pluginStateChanged(State state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Informs the callback that the plugin's polling interval has decreased.
|
||||||
|
*/
|
||||||
|
void pollingIntervalDecreased();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.briarproject.bramble.api.plugin.event;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that is broadcast when a plugin's polling interval decreases.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class PollingIntervalDecreasedEvent extends Event {
|
||||||
|
|
||||||
|
private final TransportId transportId;
|
||||||
|
|
||||||
|
public PollingIntervalDecreasedEvent(TransportId transportId) {
|
||||||
|
this.transportId = transportId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransportId getTransportId() {
|
||||||
|
return transportId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,10 +22,21 @@ interface ConnectivityChecker {
|
|||||||
* the check succeeds. If a check is already running then the observer is
|
* the check succeeds. If a check is already running then the observer is
|
||||||
* called when the check succeeds. If a connectivity check has recently
|
* called when the check succeeds. If a connectivity check has recently
|
||||||
* succeeded then the observer is called immediately.
|
* succeeded then the observer is called immediately.
|
||||||
|
* <p>
|
||||||
|
* Observers are removed after being called, or when the checker is
|
||||||
|
* {@link #destroy() destroyed}.
|
||||||
*/
|
*/
|
||||||
void checkConnectivity(MailboxProperties properties,
|
void checkConnectivity(MailboxProperties properties,
|
||||||
ConnectivityObserver o);
|
ConnectivityObserver o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an observer that was added via
|
||||||
|
* {@link #checkConnectivity(MailboxProperties, ConnectivityObserver)}. If
|
||||||
|
* there are no remaining observers and a connectivity check is running
|
||||||
|
* then the check will be cancelled.
|
||||||
|
*/
|
||||||
|
void removeObserver(ConnectivityObserver o);
|
||||||
|
|
||||||
interface ConnectivityObserver {
|
interface ConnectivityObserver {
|
||||||
void onConnectivityCheckSucceeded();
|
void onConnectivityCheckSucceeded();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,8 +80,7 @@ abstract class ConnectivityCheckerImpl implements ConnectivityChecker {
|
|||||||
> CONNECTIVITY_CHECK_FRESHNESS_MS) {
|
> CONNECTIVITY_CHECK_FRESHNESS_MS) {
|
||||||
// The last connectivity check is stale, start a new one
|
// The last connectivity check is stale, start a new one
|
||||||
connectivityObservers.add(o);
|
connectivityObservers.add(o);
|
||||||
ApiCall task =
|
ApiCall task = createConnectivityCheckTask(properties);
|
||||||
createConnectivityCheckTask(properties);
|
|
||||||
connectivityCheck = mailboxApiCaller.retryWithBackoff(task);
|
connectivityCheck = mailboxApiCaller.retryWithBackoff(task);
|
||||||
} else {
|
} else {
|
||||||
// The last connectivity check is fresh
|
// The last connectivity check is fresh
|
||||||
@@ -108,4 +107,16 @@ abstract class ConnectivityCheckerImpl implements ConnectivityChecker {
|
|||||||
o.onConnectivityCheckSucceeded();
|
o.onConnectivityCheckSucceeded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeObserver(ConnectivityObserver o) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (destroyed) return;
|
||||||
|
connectivityObservers.remove(o);
|
||||||
|
if (connectivityObservers.isEmpty() && connectivityCheck != null) {
|
||||||
|
connectivityCheck.cancel();
|
||||||
|
connectivityCheck = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@@ -24,16 +22,11 @@ class ContactMailboxConnectivityChecker extends ConnectivityCheckerImpl {
|
|||||||
@Override
|
@Override
|
||||||
ApiCall createConnectivityCheckTask(MailboxProperties properties) {
|
ApiCall createConnectivityCheckTask(MailboxProperties properties) {
|
||||||
if (properties.isOwner()) throw new IllegalArgumentException();
|
if (properties.isOwner()) throw new IllegalArgumentException();
|
||||||
return new SimpleApiCall() {
|
return new SimpleApiCall(() -> {
|
||||||
@Override
|
if (!mailboxApi.checkStatus(properties)) throw new ApiException();
|
||||||
void tryToCallApi() throws IOException, ApiException {
|
// Call the observers and cache the result
|
||||||
if (!mailboxApi.checkStatus(properties)) {
|
onConnectivityCheckSucceeded(clock.currentTimeMillis());
|
||||||
throw new ApiException();
|
});
|
||||||
}
|
|
||||||
// Call the observers and cache the result
|
|
||||||
onConnectivityCheckSucceeded(clock.currentTimeMillis());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Cancellable;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.mailbox.ConnectivityChecker.ConnectivityObserver;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||||
|
import org.briarproject.bramble.mailbox.TorReachabilityMonitor.TorReachabilityObserver;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
class ContactMailboxDownloadWorker implements MailboxWorker,
|
||||||
|
ConnectivityObserver, TorReachabilityObserver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the worker is started it waits for a connectivity check, then
|
||||||
|
* starts its first download cycle: checking the inbox, downloading and
|
||||||
|
* deleting any files, and checking again until the inbox is empty.
|
||||||
|
* <p>
|
||||||
|
* The worker then waits for our Tor hidden service to be reachable before
|
||||||
|
* starting its second download cycle. This ensures that if a contact
|
||||||
|
* tried and failed to connect to our hidden service before it was
|
||||||
|
* reachable, and therefore uploaded a file to the mailbox instead, we'll
|
||||||
|
* find the file in the second download cycle.
|
||||||
|
*/
|
||||||
|
private enum State {
|
||||||
|
CREATED,
|
||||||
|
CONNECTIVITY_CHECK,
|
||||||
|
DOWNLOAD_CYCLE_1,
|
||||||
|
WAITING_FOR_TOR,
|
||||||
|
DOWNLOAD_CYCLE_2,
|
||||||
|
FINISHED,
|
||||||
|
DESTROYED
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(ContactMailboxDownloadWorker.class.getName());
|
||||||
|
|
||||||
|
private final ConnectivityChecker connectivityChecker;
|
||||||
|
private final TorReachabilityMonitor torReachabilityMonitor;
|
||||||
|
private final MailboxApiCaller mailboxApiCaller;
|
||||||
|
private final MailboxApi mailboxApi;
|
||||||
|
private final MailboxFileManager mailboxFileManager;
|
||||||
|
private final MailboxProperties mailboxProperties;
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private State state = State.CREATED;
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
@Nullable
|
||||||
|
private Cancellable apiCall = null;
|
||||||
|
|
||||||
|
ContactMailboxDownloadWorker(
|
||||||
|
ConnectivityChecker connectivityChecker,
|
||||||
|
TorReachabilityMonitor torReachabilityMonitor,
|
||||||
|
MailboxApiCaller mailboxApiCaller,
|
||||||
|
MailboxApi mailboxApi,
|
||||||
|
MailboxFileManager mailboxFileManager,
|
||||||
|
MailboxProperties mailboxProperties) {
|
||||||
|
if (mailboxProperties.isOwner()) throw new IllegalArgumentException();
|
||||||
|
this.connectivityChecker = connectivityChecker;
|
||||||
|
this.torReachabilityMonitor = torReachabilityMonitor;
|
||||||
|
this.mailboxApiCaller = mailboxApiCaller;
|
||||||
|
this.mailboxApi = mailboxApi;
|
||||||
|
this.mailboxFileManager = mailboxFileManager;
|
||||||
|
this.mailboxProperties = mailboxProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
LOG.info("Started");
|
||||||
|
synchronized (lock) {
|
||||||
|
// Don't allow the worker to be reused
|
||||||
|
if (state != State.CREATED) throw new IllegalStateException();
|
||||||
|
state = State.CONNECTIVITY_CHECK;
|
||||||
|
}
|
||||||
|
// Avoid leaking observer in case destroy() is called concurrently
|
||||||
|
// before observer is added
|
||||||
|
connectivityChecker.checkConnectivity(mailboxProperties, this);
|
||||||
|
boolean destroyed;
|
||||||
|
synchronized (lock) {
|
||||||
|
destroyed = state == State.DESTROYED;
|
||||||
|
}
|
||||||
|
if (destroyed) connectivityChecker.removeObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
LOG.info("Destroyed");
|
||||||
|
Cancellable apiCall;
|
||||||
|
synchronized (lock) {
|
||||||
|
state = State.DESTROYED;
|
||||||
|
apiCall = this.apiCall;
|
||||||
|
this.apiCall = null;
|
||||||
|
}
|
||||||
|
if (apiCall != null) apiCall.cancel();
|
||||||
|
connectivityChecker.removeObserver(this);
|
||||||
|
torReachabilityMonitor.removeObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectivityCheckSucceeded() {
|
||||||
|
LOG.info("Connectivity check succeeded");
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state != State.CONNECTIVITY_CHECK) return;
|
||||||
|
state = State.DOWNLOAD_CYCLE_1;
|
||||||
|
// Start first download cycle
|
||||||
|
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||||
|
new SimpleApiCall(this::apiCallListInbox));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void apiCallListInbox() throws IOException, ApiException {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
}
|
||||||
|
LOG.info("Listing inbox");
|
||||||
|
List<MailboxFile> files = mailboxApi.getFiles(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()));
|
||||||
|
if (files.isEmpty()) onDownloadCycleFinished();
|
||||||
|
else downloadNextFile(new LinkedList<>(files));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDownloadCycleFinished() {
|
||||||
|
boolean addObserver = false;
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DOWNLOAD_CYCLE_1) {
|
||||||
|
LOG.info("First download cycle finished");
|
||||||
|
state = State.WAITING_FOR_TOR;
|
||||||
|
addObserver = true;
|
||||||
|
} else if (state == State.DOWNLOAD_CYCLE_2) {
|
||||||
|
LOG.info("Second download cycle finished");
|
||||||
|
state = State.FINISHED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (addObserver) {
|
||||||
|
// Avoid leaking observer in case destroy() is called concurrently
|
||||||
|
// before observer is added
|
||||||
|
torReachabilityMonitor.addOneShotObserver(this);
|
||||||
|
boolean destroyed;
|
||||||
|
synchronized (lock) {
|
||||||
|
destroyed = state == State.DESTROYED;
|
||||||
|
}
|
||||||
|
if (destroyed) torReachabilityMonitor.removeObserver(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadNextFile(Queue<MailboxFile> queue) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
MailboxFile file = queue.remove();
|
||||||
|
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||||
|
new SimpleApiCall(() -> apiCallDownloadFile(file, queue)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void apiCallDownloadFile(MailboxFile file,
|
||||||
|
Queue<MailboxFile> queue) throws IOException, ApiException {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
}
|
||||||
|
LOG.info("Downloading file");
|
||||||
|
File tempFile = mailboxFileManager.createTempFileForDownload();
|
||||||
|
try {
|
||||||
|
mailboxApi.getFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()),
|
||||||
|
file.name, tempFile);
|
||||||
|
} catch (IOException | ApiException e) {
|
||||||
|
if (!tempFile.delete()) {
|
||||||
|
LOG.warning("Failed to delete temporary file");
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
mailboxFileManager.handleDownloadedFile(tempFile);
|
||||||
|
deleteFile(file, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteFile(MailboxFile file, Queue<MailboxFile> queue) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||||
|
new SimpleApiCall(() -> apiCallDeleteFile(file, queue)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void apiCallDeleteFile(MailboxFile file, Queue<MailboxFile> queue)
|
||||||
|
throws IOException, ApiException {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mailboxApi.deleteFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()), file.name);
|
||||||
|
} catch (TolerableFailureException e) {
|
||||||
|
// Catch this so we can continue to the next file
|
||||||
|
logException(LOG, INFO, e);
|
||||||
|
}
|
||||||
|
if (queue.isEmpty()) {
|
||||||
|
// List the inbox again to check for files that may have arrived
|
||||||
|
// while we were downloading
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||||
|
new SimpleApiCall(this::apiCallListInbox));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
downloadNextFile(queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTorReachable() {
|
||||||
|
LOG.info("Our Tor hidden service is reachable");
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state != State.WAITING_FOR_TOR) return;
|
||||||
|
state = State.DOWNLOAD_CYCLE_2;
|
||||||
|
// Start second download cycle
|
||||||
|
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||||
|
new SimpleApiCall(this::apiCallListInbox));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,8 @@ interface MailboxApiCaller {
|
|||||||
* Asynchronously calls the given API call on the {@link IoExecutor},
|
* Asynchronously calls the given API call on the {@link IoExecutor},
|
||||||
* automatically retrying at increasing intervals until the API call
|
* automatically retrying at increasing intervals until the API call
|
||||||
* returns false or retries are cancelled.
|
* returns false or retries are cancelled.
|
||||||
|
* <p>
|
||||||
|
* This method is safe to call while holding a lock.
|
||||||
*
|
*
|
||||||
* @return A {@link Cancellable} that can be used to cancel any future
|
* @return A {@link Cancellable} that can be used to cancel any future
|
||||||
* retries.
|
* retries.
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A worker that downloads files from a contact's mailbox.
|
||||||
|
*/
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
interface MailboxWorker {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously starts the worker.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the worker and cancels any pending tasks or retries.
|
||||||
|
*/
|
||||||
|
void destroy();
|
||||||
|
}
|
||||||
@@ -16,17 +16,20 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
|||||||
* Convenience class for making simple API calls that don't return values.
|
* Convenience class for making simple API calls that don't return values.
|
||||||
*/
|
*/
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public abstract class SimpleApiCall implements ApiCall {
|
class SimpleApiCall implements ApiCall {
|
||||||
|
|
||||||
private static final Logger LOG = getLogger(SimpleApiCall.class.getName());
|
private static final Logger LOG = getLogger(SimpleApiCall.class.getName());
|
||||||
|
|
||||||
abstract void tryToCallApi()
|
private final Attempt attempt;
|
||||||
throws IOException, ApiException, TolerableFailureException;
|
|
||||||
|
SimpleApiCall(Attempt attempt) {
|
||||||
|
this.attempt = attempt;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean callApi() {
|
public boolean callApi() {
|
||||||
try {
|
try {
|
||||||
tryToCallApi();
|
attempt.tryToCallApi();
|
||||||
return false; // Succeeded, don't retry
|
return false; // Succeeded, don't retry
|
||||||
} catch (IOException | ApiException e) {
|
} catch (IOException | ApiException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
@@ -36,4 +39,17 @@ public abstract class SimpleApiCall implements ApiCall {
|
|||||||
return false; // Failed tolerably, don't retry
|
return false; // Failed tolerably, don't retry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Attempt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a single attempt to call an API endpoint. If this method
|
||||||
|
* throws an {@link IOException} or an {@link ApiException}, the call
|
||||||
|
* will be retried. If it throws a {@link TolerableFailureException}
|
||||||
|
* or returns without throwing an exception, the call will not be
|
||||||
|
* retried.
|
||||||
|
*/
|
||||||
|
void tryToCallApi()
|
||||||
|
throws IOException, ApiException, TolerableFailureException;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ interface TorReachabilityMonitor {
|
|||||||
*/
|
*/
|
||||||
void addOneShotObserver(TorReachabilityObserver o);
|
void addOneShotObserver(TorReachabilityObserver o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an observer that was added via
|
||||||
|
* {@link #addOneShotObserver(TorReachabilityObserver)}.
|
||||||
|
*/
|
||||||
|
void removeObserver(TorReachabilityObserver o);
|
||||||
|
|
||||||
interface TorReachabilityObserver {
|
interface TorReachabilityObserver {
|
||||||
|
|
||||||
void onTorReachable();
|
void onTorReachable();
|
||||||
|
|||||||
@@ -87,6 +87,14 @@ class TorReachabilityMonitorImpl
|
|||||||
if (callNow) o.onTorReachable();
|
if (callNow) o.onTorReachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeObserver(TorReachabilityObserver o) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (destroyed) return;
|
||||||
|
observers.remove(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof TransportActiveEvent) {
|
if (e instanceof TransportActiveEvent) {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
@@ -11,8 +13,9 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
class BackoffFactoryImpl implements BackoffFactory {
|
class BackoffFactoryImpl implements BackoffFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Backoff createBackoff(int minInterval, int maxInterval,
|
public Backoff createBackoff(EventBus eventBus, TransportId transportId,
|
||||||
double base) {
|
int minInterval, int maxInterval, double base) {
|
||||||
return new BackoffImpl(minInterval, maxInterval, base);
|
return new BackoffImpl(eventBus, transportId, minInterval, maxInterval,
|
||||||
|
base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.PollingIntervalDecreasedEvent;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@@ -11,11 +14,16 @@ import javax.annotation.concurrent.ThreadSafe;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class BackoffImpl implements Backoff {
|
class BackoffImpl implements Backoff {
|
||||||
|
|
||||||
|
private final EventBus eventBus;
|
||||||
|
private final TransportId transportId;
|
||||||
private final int minInterval, maxInterval;
|
private final int minInterval, maxInterval;
|
||||||
private final double base;
|
private final double base;
|
||||||
private final AtomicInteger backoff;
|
private final AtomicInteger backoff;
|
||||||
|
|
||||||
BackoffImpl(int minInterval, int maxInterval, double base) {
|
BackoffImpl(EventBus eventBus, TransportId transportId,
|
||||||
|
int minInterval, int maxInterval, double base) {
|
||||||
|
this.eventBus = eventBus;
|
||||||
|
this.transportId = transportId;
|
||||||
this.minInterval = minInterval;
|
this.minInterval = minInterval;
|
||||||
this.maxInterval = maxInterval;
|
this.maxInterval = maxInterval;
|
||||||
this.base = base;
|
this.base = base;
|
||||||
@@ -37,6 +45,9 @@ class BackoffImpl implements Backoff {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
backoff.set(0);
|
int old = backoff.getAndSet(0);
|
||||||
|
if (old > 0) {
|
||||||
|
eventBus.broadcast(new PollingIntervalDecreasedEvent(transportId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.PollingIntervalDecreasedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
|
||||||
@@ -362,6 +363,11 @@ class PluginManagerImpl implements PluginManager, Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pollingIntervalDecreased() {
|
||||||
|
eventBus.broadcast(new PollingIntervalDecreasedEvent(id));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleConnection(DuplexTransportConnection d) {
|
public void handleConnection(DuplexTransportConnection d) {
|
||||||
connectionManager.manageIncomingConnection(id, d);
|
connectionManager.manageIncomingConnection(id, d);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
import org.briarproject.bramble.api.plugin.event.PollingIntervalDecreasedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
||||||
@@ -107,10 +107,14 @@ class PollerImpl implements Poller, EventListener {
|
|||||||
if (!c.isIncoming() && c.isException()) {
|
if (!c.isIncoming() && c.isException()) {
|
||||||
connectToContact(c.getContactId(), c.getTransportId());
|
connectToContact(c.getContactId(), c.getTransportId());
|
||||||
}
|
}
|
||||||
} else if (e instanceof ConnectionOpenedEvent) {
|
} else if (e instanceof PollingIntervalDecreasedEvent) {
|
||||||
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
|
PollingIntervalDecreasedEvent p = (PollingIntervalDecreasedEvent) e;
|
||||||
// Reschedule polling, the polling interval may have decreased
|
TransportId t = p.getTransportId();
|
||||||
reschedule(c.getTransportId());
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Polling interval decreased for " + t);
|
||||||
|
}
|
||||||
|
// Reschedule polling
|
||||||
|
reschedule(t);
|
||||||
} else if (e instanceof TransportActiveEvent) {
|
} else if (e instanceof TransportActiveEvent) {
|
||||||
TransportActiveEvent t = (TransportActiveEvent) e;
|
TransportActiveEvent t = (TransportActiveEvent) e;
|
||||||
// Poll the newly activated transport
|
// Poll the newly activated transport
|
||||||
@@ -228,7 +232,7 @@ class PollerImpl implements Poller, EventListener {
|
|||||||
if (!connected.contains(c))
|
if (!connected.contains(c))
|
||||||
properties.add(new Pair<>(e.getValue(), new Handler(c, t)));
|
properties.add(new Pair<>(e.getValue(), new Handler(c, t)));
|
||||||
}
|
}
|
||||||
if (!properties.isEmpty()) p.poll(properties);
|
p.poll(properties);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
|
|||||||
@Override
|
@Override
|
||||||
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||||
properties) {
|
properties) {
|
||||||
if (getState() != ACTIVE) return;
|
if (properties.isEmpty() || getState() != ACTIVE) return;
|
||||||
backoff.increment();
|
backoff.increment();
|
||||||
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
||||||
connect(p.getFirst(), p.getSecond());
|
connect(p.getFirst(), p.getSecond());
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexPlugin createPlugin(PluginCallback callback) {
|
public DuplexPlugin createPlugin(PluginCallback callback) {
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(eventBus, ID,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, wakefulIoExecutor,
|
LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, wakefulIoExecutor,
|
||||||
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME,
|
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME,
|
||||||
CONNECTION_TIMEOUT);
|
CONNECTION_TIMEOUT);
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||||
properties) {
|
properties) {
|
||||||
if (getState() != ACTIVE) return;
|
if (properties.isEmpty() || getState() != ACTIVE) return;
|
||||||
backoff.increment();
|
backoff.increment();
|
||||||
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
||||||
connect(p.getFirst(), p.getSecond());
|
connect(p.getFirst(), p.getSecond());
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexPlugin createPlugin(PluginCallback callback) {
|
public DuplexPlugin createPlugin(PluginCallback callback) {
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(eventBus, ID,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
PortMapper portMapper = new PortMapperImpl(shutdownManager);
|
PortMapper portMapper = new PortMapperImpl(shutdownManager);
|
||||||
WanTcpPlugin plugin = new WanTcpPlugin(ioExecutor, wakefulIoExecutor,
|
WanTcpPlugin plugin = new WanTcpPlugin(ioExecutor, wakefulIoExecutor,
|
||||||
backoff, portMapper, callback, MAX_LATENCY, MAX_IDLE_TIME,
|
backoff, portMapper, callback, MAX_LATENCY, MAX_IDLE_TIME,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public interface CircumventionProvider {
|
|||||||
* Countries where bridge connections are likely to work.
|
* Countries where bridge connections are likely to work.
|
||||||
* Should be a subset of {@link #BLOCKED} and the union of
|
* Should be a subset of {@link #BLOCKED} and the union of
|
||||||
* {@link #DEFAULT_BRIDGES}, {@link #NON_DEFAULT_BRIDGES} and
|
* {@link #DEFAULT_BRIDGES}, {@link #NON_DEFAULT_BRIDGES} and
|
||||||
* {@link #MEEK_BRIDGES}.
|
* {@link #DPI_BRIDGES}.
|
||||||
*/
|
*/
|
||||||
String[] BRIDGES = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"};
|
String[] BRIDGES = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"};
|
||||||
|
|
||||||
@@ -44,10 +44,10 @@ public interface CircumventionProvider {
|
|||||||
String[] NON_DEFAULT_BRIDGES = {"BY", "RU", "TM"};
|
String[] NON_DEFAULT_BRIDGES = {"BY", "RU", "TM"};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Countries where obfs4 and vanilla bridges won't work and meek is needed.
|
* Countries where vanilla bridges are blocked via DPI but non-default
|
||||||
* Should be a subset of {@link #BRIDGES}.
|
* obfs4 bridges and meek may work. Should be a subset of {@link #BRIDGES}.
|
||||||
*/
|
*/
|
||||||
String[] MEEK_BRIDGES = {"CN", "IR"};
|
String[] DPI_BRIDGES = {"CN", "IR"};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if vanilla Tor connections are blocked in the given country.
|
* Returns true if vanilla Tor connections are blocked in the given country.
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
||||||
@@ -35,8 +34,8 @@ class CircumventionProviderImpl implements CircumventionProvider {
|
|||||||
new HashSet<>(asList(DEFAULT_BRIDGES));
|
new HashSet<>(asList(DEFAULT_BRIDGES));
|
||||||
private static final Set<String> NON_DEFAULT_BRIDGE_COUNTRIES =
|
private static final Set<String> NON_DEFAULT_BRIDGE_COUNTRIES =
|
||||||
new HashSet<>(asList(NON_DEFAULT_BRIDGES));
|
new HashSet<>(asList(NON_DEFAULT_BRIDGES));
|
||||||
private static final Set<String> MEEK_COUNTRIES =
|
private static final Set<String> DPI_COUNTRIES =
|
||||||
new HashSet<>(asList(MEEK_BRIDGES));
|
new HashSet<>(asList(DPI_BRIDGES));
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CircumventionProviderImpl() {
|
CircumventionProviderImpl() {
|
||||||
@@ -58,8 +57,8 @@ class CircumventionProviderImpl implements CircumventionProvider {
|
|||||||
return asList(DEFAULT_OBFS4, VANILLA);
|
return asList(DEFAULT_OBFS4, VANILLA);
|
||||||
} else if (NON_DEFAULT_BRIDGE_COUNTRIES.contains(countryCode)) {
|
} else if (NON_DEFAULT_BRIDGE_COUNTRIES.contains(countryCode)) {
|
||||||
return asList(NON_DEFAULT_OBFS4, VANILLA);
|
return asList(NON_DEFAULT_OBFS4, VANILLA);
|
||||||
} else if (MEEK_COUNTRIES.contains(countryCode)) {
|
} else if (DPI_COUNTRIES.contains(countryCode)) {
|
||||||
return singletonList(MEEK);
|
return asList(NON_DEFAULT_OBFS4, MEEK);
|
||||||
} else {
|
} else {
|
||||||
return asList(DEFAULT_OBFS4, VANILLA);
|
return asList(DEFAULT_OBFS4, VANILLA);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
|
|||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.PluginException;
|
import org.briarproject.bramble.api.plugin.PluginException;
|
||||||
@@ -26,6 +25,7 @@ import org.briarproject.bramble.api.plugin.TorConstants;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
||||||
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
||||||
@@ -67,6 +67,7 @@ import javax.net.SocketFactory;
|
|||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
@@ -107,7 +108,7 @@ import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
|||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||||
|
|
||||||
private static final Logger LOG = getLogger(TorPlugin.class.getName());
|
protected static final Logger LOG = getLogger(TorPlugin.class.getName());
|
||||||
|
|
||||||
private static final String[] EVENTS = {
|
private static final String[] EVENTS = {
|
||||||
"CIRC",
|
"CIRC",
|
||||||
@@ -124,14 +125,53 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
|
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
|
||||||
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
|
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
|
||||||
|
|
||||||
private final Executor ioExecutor, wakefulIoExecutor;
|
/**
|
||||||
|
* After this many consecutive successful connections to our own hidden
|
||||||
|
* service we consider the network to be stable.
|
||||||
|
* <p>
|
||||||
|
* This constant times {@link #POLLING_INTERVAL_UNSTABLE} should be
|
||||||
|
* greater than {@link #POLLING_INTERVAL_STABLE}. This ensures that if
|
||||||
|
* we experience a network outage that isn't detected by the
|
||||||
|
* {@link NetworkManager}, and if one of our contacts comes online during
|
||||||
|
* the outage, then either the outage lasts longer than
|
||||||
|
* {@link #POLLING_INTERVAL_STABLE}, in which case we detect the outage
|
||||||
|
* by failing to connect to our own hidden service, or the outage ends
|
||||||
|
* before the contact considers their own network connection to be stable,
|
||||||
|
* in which case the contact is still trying to connect to us when the
|
||||||
|
* outage ends. Either way, we don't end up in a situation where both we
|
||||||
|
* and the contact consider our network connections to be stable and stop
|
||||||
|
* trying to connect to each other, despite both being online.
|
||||||
|
*/
|
||||||
|
private static final int STABLE_NETWORK_THRESHOLD = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After this many consecutive failed connections to our own hidden service
|
||||||
|
* we consider our connection to the Tor network to be broken.
|
||||||
|
*/
|
||||||
|
private static final int BROKEN_NETWORK_THRESHOLD = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How often to poll our own hidden service when the network is considered
|
||||||
|
* to be stable.
|
||||||
|
*/
|
||||||
|
private static final int POLLING_INTERVAL_STABLE =
|
||||||
|
(int) MINUTES.toMillis(10);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How often to poll our own hidden service and our contacts' hidden
|
||||||
|
* services when the network is considered to be unstable.
|
||||||
|
*/
|
||||||
|
private static final int POLLING_INTERVAL_UNSTABLE =
|
||||||
|
(int) MINUTES.toMillis(2);
|
||||||
|
|
||||||
|
protected final Executor ioExecutor;
|
||||||
|
private final Executor wakefulIoExecutor;
|
||||||
private final Executor connectionStatusExecutor;
|
private final Executor connectionStatusExecutor;
|
||||||
private final NetworkManager networkManager;
|
private final NetworkManager networkManager;
|
||||||
private final LocationUtils locationUtils;
|
private final LocationUtils locationUtils;
|
||||||
private final SocketFactory torSocketFactory;
|
private final SocketFactory torSocketFactory;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final BatteryManager batteryManager;
|
private final BatteryManager batteryManager;
|
||||||
private final Backoff backoff;
|
|
||||||
private final TorRendezvousCrypto torRendezvousCrypto;
|
private final TorRendezvousCrypto torRendezvousCrypto;
|
||||||
private final PluginCallback callback;
|
private final PluginCallback callback;
|
||||||
private final String architecture;
|
private final String architecture;
|
||||||
@@ -151,6 +191,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private volatile Socket controlSocket = null;
|
private volatile Socket controlSocket = null;
|
||||||
private volatile TorControlConnection controlConnection = null;
|
private volatile TorControlConnection controlConnection = null;
|
||||||
private volatile Settings settings = null;
|
private volatile Settings settings = null;
|
||||||
|
private volatile String ownOnion = null;
|
||||||
|
|
||||||
protected abstract int getProcessId();
|
protected abstract int getProcessId();
|
||||||
|
|
||||||
@@ -165,7 +206,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
TorRendezvousCrypto torRendezvousCrypto,
|
||||||
PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture,
|
String architecture,
|
||||||
@@ -183,7 +223,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
this.resourceProvider = resourceProvider;
|
this.resourceProvider = resourceProvider;
|
||||||
this.circumventionProvider = circumventionProvider;
|
this.circumventionProvider = circumventionProvider;
|
||||||
this.batteryManager = batteryManager;
|
this.batteryManager = batteryManager;
|
||||||
this.backoff = backoff;
|
|
||||||
this.torRendezvousCrypto = torRendezvousCrypto;
|
this.torRendezvousCrypto = torRendezvousCrypto;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.architecture = architecture;
|
this.architecture = architecture;
|
||||||
@@ -254,34 +293,15 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
Map<String, String> env = pb.environment();
|
Map<String, String> env = pb.environment();
|
||||||
env.put("HOME", torDirectory.getAbsolutePath());
|
env.put("HOME", torDirectory.getAbsolutePath());
|
||||||
pb.directory(torDirectory);
|
pb.directory(torDirectory);
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
try {
|
try {
|
||||||
torProcess = pb.start();
|
torProcess = pb.start();
|
||||||
} catch (SecurityException | IOException e) {
|
} catch (SecurityException | IOException e) {
|
||||||
throw new PluginException(e);
|
throw new PluginException(e);
|
||||||
}
|
}
|
||||||
// Log the process's standard output until it detaches
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
Scanner stdout = new Scanner(torProcess.getInputStream());
|
|
||||||
Scanner stderr = new Scanner(torProcess.getErrorStream());
|
|
||||||
while (stdout.hasNextLine() || stderr.hasNextLine()) {
|
|
||||||
if (stdout.hasNextLine()) {
|
|
||||||
LOG.info(stdout.nextLine());
|
|
||||||
}
|
|
||||||
if (stderr.hasNextLine()) {
|
|
||||||
LOG.info(stderr.nextLine());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stdout.close();
|
|
||||||
stderr.close();
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
// Wait for the process to detach or exit
|
// Wait for the Tor process to start
|
||||||
int exit = torProcess.waitFor();
|
waitForTorToStart(torProcess);
|
||||||
if (exit != 0) {
|
|
||||||
if (LOG.isLoggable(WARNING))
|
|
||||||
LOG.warning("Tor exited with value " + exit);
|
|
||||||
throw new PluginException();
|
|
||||||
}
|
|
||||||
// Wait for the auth cookie file to be created/updated
|
// Wait for the auth cookie file to be created/updated
|
||||||
long start = clock.currentTimeMillis();
|
long start = clock.currentTimeMillis();
|
||||||
while (cookieFile.length() < 32) {
|
while (cookieFile.length() < 32) {
|
||||||
@@ -397,7 +417,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
return zin;
|
return zin;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void append(StringBuilder strb, String name, int value) {
|
private static void append(StringBuilder strb, String name, Object value) {
|
||||||
strb.append(name);
|
strb.append(name);
|
||||||
strb.append(" ");
|
strb.append(" ");
|
||||||
strb.append(value);
|
strb.append(value);
|
||||||
@@ -405,13 +425,17 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getConfigInputStream() {
|
private InputStream getConfigInputStream() {
|
||||||
|
File dataDirectory = new File(torDirectory, ".tor");
|
||||||
StringBuilder strb = new StringBuilder();
|
StringBuilder strb = new StringBuilder();
|
||||||
append(strb, "ControlPort", torControlPort);
|
append(strb, "ControlPort", torControlPort);
|
||||||
append(strb, "CookieAuthentication", 1);
|
append(strb, "CookieAuthentication", 1);
|
||||||
|
append(strb, "DataDirectory", dataDirectory.getAbsolutePath());
|
||||||
append(strb, "DisableNetwork", 1);
|
append(strb, "DisableNetwork", 1);
|
||||||
append(strb, "RunAsDaemon", 1);
|
append(strb, "RunAsDaemon", 1);
|
||||||
append(strb, "SafeSocks", 1);
|
append(strb, "SafeSocks", 1);
|
||||||
append(strb, "SocksPort", torSocksPort);
|
append(strb, "SocksPort", torSocksPort);
|
||||||
|
strb.append("GeoIPFile\n");
|
||||||
|
strb.append("GeoIPv6File\n");
|
||||||
//noinspection CharsetObjectCanBeUsed
|
//noinspection CharsetObjectCanBeUsed
|
||||||
return new ByteArrayInputStream(
|
return new ByteArrayInputStream(
|
||||||
strb.toString().getBytes(Charset.forName("UTF-8")));
|
strb.toString().getBytes(Charset.forName("UTF-8")));
|
||||||
@@ -442,6 +466,23 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void waitForTorToStart(Process torProcess)
|
||||||
|
throws InterruptedException, PluginException {
|
||||||
|
Scanner stdout = new Scanner(torProcess.getInputStream());
|
||||||
|
// Log the first line of stdout (contains Tor and library versions)
|
||||||
|
if (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
||||||
|
// Read the process's stdout (and redirected stderr) until it detaches
|
||||||
|
while (stdout.hasNextLine()) stdout.nextLine();
|
||||||
|
stdout.close();
|
||||||
|
// Wait for the process to detach or exit
|
||||||
|
int exit = torProcess.waitFor();
|
||||||
|
if (exit != 0) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.warning("Tor exited with value " + exit);
|
||||||
|
throw new PluginException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void bind() {
|
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
|
||||||
@@ -471,7 +512,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
callback.mergeSettings(s);
|
callback.mergeSettings(s);
|
||||||
// Create a hidden service if necessary
|
// Create a hidden service if necessary
|
||||||
ioExecutor.execute(() -> publishHiddenService(localPort));
|
ioExecutor.execute(() -> publishHiddenService(localPort));
|
||||||
backoff.reset();
|
|
||||||
// Accept incoming hidden service connections from Tor
|
// Accept incoming hidden service connections from Tor
|
||||||
acceptContactConnections(ss);
|
acceptContactConnections(ss);
|
||||||
});
|
});
|
||||||
@@ -510,6 +550,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String onion3 = response.get(HS_ADDRESS);
|
String onion3 = response.get(HS_ADDRESS);
|
||||||
|
ownOnion = onion3;
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("V3 hidden service " + scrubOnion(onion3));
|
LOG.info("V3 hidden service " + scrubOnion(onion3));
|
||||||
}
|
}
|
||||||
@@ -538,7 +579,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG.info("Connection received");
|
LOG.info("Connection received");
|
||||||
backoff.reset();
|
|
||||||
callback.handleConnection(new TorTransportConnection(this, s));
|
callback.handleConnection(new TorTransportConnection(this, s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -615,14 +655,58 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPollingInterval() {
|
public int getPollingInterval() {
|
||||||
return backoff.getPollingInterval();
|
if (state.isNetworkStable()) {
|
||||||
|
LOG.info("Using stable polling interval");
|
||||||
|
return POLLING_INTERVAL_STABLE;
|
||||||
|
} else {
|
||||||
|
LOG.info("Using unstable polling interval");
|
||||||
|
return POLLING_INTERVAL_UNSTABLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||||
properties) {
|
properties) {
|
||||||
if (getState() != ACTIVE) return;
|
if (getState() != ACTIVE) return;
|
||||||
backoff.increment();
|
String ownOnion = this.ownOnion;
|
||||||
|
if (ownOnion == null) {
|
||||||
|
// Our own hidden service hasn't been created yet
|
||||||
|
pollContacts(properties);
|
||||||
|
} else {
|
||||||
|
// If the network is unstable, poll our contacts
|
||||||
|
boolean stable = state.isNetworkStable();
|
||||||
|
if (!stable) pollContacts(properties);
|
||||||
|
// Poll our own hidden service to check if the network is stable
|
||||||
|
wakefulIoExecutor.execute(() -> {
|
||||||
|
LOG.info("Connecting to own hidden service");
|
||||||
|
TransportProperties p = new TransportProperties();
|
||||||
|
p.put(PROP_ONION_V3, ownOnion);
|
||||||
|
DuplexTransportConnection d = createConnection(p);
|
||||||
|
if (d == null) {
|
||||||
|
LOG.info("Could not connect to own hidden service");
|
||||||
|
state.onStabilityCheckFailed();
|
||||||
|
// If the network was previously considered stable then
|
||||||
|
// we didn't poll our contacts above, so poll them now
|
||||||
|
if (stable) pollContacts(properties);
|
||||||
|
} else {
|
||||||
|
LOG.info("Connected to own hidden service");
|
||||||
|
// Close the connection (this will cause the other end of
|
||||||
|
// the connection to log an EOFException)
|
||||||
|
try {
|
||||||
|
d.getWriter().dispose(false);
|
||||||
|
d.getReader().dispose(false, false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
state.onStabilityCheckSucceeded();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pollContacts(
|
||||||
|
Collection<Pair<TransportProperties, ConnectionHandler>> properties) {
|
||||||
|
if (properties.isEmpty() || getState() != ACTIVE) return;
|
||||||
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
||||||
connect(p.getFirst(), p.getSecond());
|
connect(p.getFirst(), p.getSecond());
|
||||||
}
|
}
|
||||||
@@ -631,10 +715,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private void connect(TransportProperties p, ConnectionHandler h) {
|
private void connect(TransportProperties p, ConnectionHandler h) {
|
||||||
wakefulIoExecutor.execute(() -> {
|
wakefulIoExecutor.execute(() -> {
|
||||||
DuplexTransportConnection d = createConnection(p);
|
DuplexTransportConnection d = createConnection(p);
|
||||||
if (d != null) {
|
if (d != null) h.handleConnection(d);
|
||||||
backoff.reset();
|
|
||||||
h.handleConnection(d);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,7 +838,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
// DisableNetwork, set our circuitBuilt flag if not already set
|
// DisableNetwork, set our circuitBuilt flag if not already set
|
||||||
if (status.equals("BUILT") && !state.getAndSetCircuitBuilt(true)) {
|
if (status.equals("BUILT") && !state.getAndSetCircuitBuilt(true)) {
|
||||||
LOG.info("Circuit built");
|
LOG.info("Circuit built");
|
||||||
backoff.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -784,6 +864,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void message(String severity, String msg) {
|
public void message(String severity, String msg) {
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
|
if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
|
||||||
|
if (msg.startsWith("Switching to guard context")) {
|
||||||
|
state.resetNetworkStability();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -810,12 +893,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
if (msg.startsWith("BOOTSTRAP PROGRESS=100")) {
|
if (msg.startsWith("BOOTSTRAP PROGRESS=100")) {
|
||||||
LOG.info("Bootstrapped");
|
LOG.info("Bootstrapped");
|
||||||
state.setBootstrapped();
|
state.setBootstrapped();
|
||||||
backoff.reset();
|
|
||||||
} else if (msg.startsWith("CIRCUIT_ESTABLISHED")) {
|
} else if (msg.startsWith("CIRCUIT_ESTABLISHED")) {
|
||||||
if (!state.getAndSetCircuitBuilt(true)) {
|
if (!state.getAndSetCircuitBuilt(true)) LOG.info("Circuit built");
|
||||||
LOG.info("Circuit built");
|
|
||||||
backoff.reset();
|
|
||||||
}
|
|
||||||
} else if (msg.startsWith("CIRCUIT_NOT_ESTABLISHED")) {
|
} else if (msg.startsWith("CIRCUIT_NOT_ESTABLISHED")) {
|
||||||
if (state.getAndSetCircuitBuilt(false)) {
|
if (state.getAndSetCircuitBuilt(false)) {
|
||||||
LOG.info("Circuit not built");
|
LOG.info("Circuit not built");
|
||||||
@@ -860,7 +939,15 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof SettingsUpdatedEvent) {
|
if (e instanceof ConnectionClosedEvent) {
|
||||||
|
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
|
||||||
|
if (c.getTransportId().equals(getId())
|
||||||
|
&& !c.isIncoming() && c.isException()) {
|
||||||
|
LOG.info("Outgoing connection closed with exception");
|
||||||
|
// The failure may indicate that the network is unstable
|
||||||
|
state.resetNetworkStability();
|
||||||
|
}
|
||||||
|
} else if (e instanceof SettingsUpdatedEvent) {
|
||||||
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
|
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
|
||||||
if (s.getNamespace().equals(ID.getString())) {
|
if (s.getNamespace().equals(ID.getString())) {
|
||||||
LOG.info("Tor settings updated");
|
LOG.info("Tor settings updated");
|
||||||
@@ -877,6 +964,18 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void disableAndReenableNetwork() {
|
||||||
|
connectionStatusExecutor.execute(() -> {
|
||||||
|
try {
|
||||||
|
if (state.isTorRunning()) enableNetwork(false);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logException(LOG, WARNING, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
updateConnectionStatus(networkManager.getNetworkStatus(),
|
||||||
|
batteryManager.isCharging());
|
||||||
|
}
|
||||||
|
|
||||||
private void updateConnectionStatus(NetworkStatus status,
|
private void updateConnectionStatus(NetworkStatus status,
|
||||||
boolean charging) {
|
boolean charging) {
|
||||||
connectionStatusExecutor.execute(() -> {
|
connectionStatusExecutor.execute(() -> {
|
||||||
@@ -1005,6 +1104,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private int reasonsDisabled = 0;
|
private int reasonsDisabled = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of consecutive successful (positive) or failed (negative)
|
||||||
|
* connections to our own hidden service.
|
||||||
|
*/
|
||||||
|
@GuardedBy("this")
|
||||||
|
private int networkStability = 0;
|
||||||
|
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
@Nullable
|
@Nullable
|
||||||
private ServerSocket serverSocket = null;
|
private ServerSocket serverSocket = null;
|
||||||
@@ -1099,6 +1205,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
logOrConnections();
|
logOrConnections();
|
||||||
if (orConnectionsConnected == 0 && oldConnected != 0) {
|
if (orConnectionsConnected == 0 && oldConnected != 0) {
|
||||||
|
resetNetworkStability();
|
||||||
callback.pluginStateChanged(getState());
|
callback.pluginStateChanged(getState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1109,5 +1216,43 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
LOG.info(orConnectionsConnected + " OR connections connected");
|
LOG.info(orConnectionsConnected + " OR connections connected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private synchronized boolean isNetworkStable() {
|
||||||
|
return networkStability >= STABLE_NETWORK_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void onStabilityCheckSucceeded() {
|
||||||
|
if (networkStability <= 0) networkStability = 1;
|
||||||
|
else networkStability++;
|
||||||
|
logNetworkStability();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void onStabilityCheckFailed() {
|
||||||
|
if (networkStability >= 0) networkStability = -1;
|
||||||
|
else networkStability--;
|
||||||
|
if (networkStability <= -BROKEN_NETWORK_THRESHOLD) {
|
||||||
|
LOG.warning("Connection to Tor appears to be broken");
|
||||||
|
resetNetworkStability();
|
||||||
|
disableAndReenableNetwork();
|
||||||
|
} else {
|
||||||
|
logNetworkStability();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void resetNetworkStability() {
|
||||||
|
int old = networkStability;
|
||||||
|
networkStability = 0;
|
||||||
|
logNetworkStability();
|
||||||
|
if (old >= STABLE_NETWORK_THRESHOLD) {
|
||||||
|
callback.pollingIntervalDecreased();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private void logNetworkStability() {
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Network stability score " + networkStability);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.TorConstants;
|
import org.briarproject.bramble.api.plugin.TorConstants;
|
||||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||||
@@ -41,16 +39,12 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
|
|
||||||
protected static final int MAX_LATENCY = 30 * 1000; // 30 seconds
|
protected static final int MAX_LATENCY = 30 * 1000; // 30 seconds
|
||||||
protected static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
|
protected static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
|
||||||
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
|
|
||||||
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
|
|
||||||
private static final double BACKOFF_BASE = 1.2;
|
|
||||||
|
|
||||||
protected final Executor ioExecutor, wakefulIoExecutor;
|
protected final Executor ioExecutor, wakefulIoExecutor;
|
||||||
protected final NetworkManager networkManager;
|
protected final NetworkManager networkManager;
|
||||||
protected final LocationUtils locationUtils;
|
protected final LocationUtils locationUtils;
|
||||||
protected final EventBus eventBus;
|
protected final EventBus eventBus;
|
||||||
protected final SocketFactory torSocketFactory;
|
protected final SocketFactory torSocketFactory;
|
||||||
protected final BackoffFactory backoffFactory;
|
|
||||||
protected final ResourceProvider resourceProvider;
|
protected final ResourceProvider resourceProvider;
|
||||||
protected final CircumventionProvider circumventionProvider;
|
protected final CircumventionProvider circumventionProvider;
|
||||||
protected final BatteryManager batteryManager;
|
protected final BatteryManager batteryManager;
|
||||||
@@ -66,7 +60,6 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory,
|
|
||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
@@ -81,7 +74,6 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
this.locationUtils = locationUtils;
|
this.locationUtils = locationUtils;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.torSocketFactory = torSocketFactory;
|
this.torSocketFactory = torSocketFactory;
|
||||||
this.backoffFactory = backoffFactory;
|
|
||||||
this.resourceProvider = resourceProvider;
|
this.resourceProvider = resourceProvider;
|
||||||
this.circumventionProvider = circumventionProvider;
|
this.circumventionProvider = circumventionProvider;
|
||||||
this.batteryManager = batteryManager;
|
this.batteryManager = batteryManager;
|
||||||
@@ -95,7 +87,7 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
@Nullable
|
@Nullable
|
||||||
abstract String getArchitectureForTorBinary();
|
abstract String getArchitectureForTorBinary();
|
||||||
|
|
||||||
abstract TorPlugin createPluginInstance(Backoff backoff,
|
abstract TorPlugin createPluginInstance(
|
||||||
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
||||||
String architecture);
|
String architecture);
|
||||||
|
|
||||||
@@ -122,11 +114,9 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
LOG.info("The selected architecture for Tor is " + architecture);
|
LOG.info("The selected architecture for Tor is " + architecture);
|
||||||
}
|
}
|
||||||
|
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto =
|
TorRendezvousCrypto torRendezvousCrypto =
|
||||||
new TorRendezvousCryptoImpl(crypto);
|
new TorRendezvousCryptoImpl(crypto);
|
||||||
TorPlugin plugin = createPluginInstance(backoff, torRendezvousCrypto,
|
TorPlugin plugin = createPluginInstance(torRendezvousCrypto,
|
||||||
callback, architecture);
|
callback, architecture);
|
||||||
eventBus.addListener(plugin);
|
eventBus.addListener(plugin);
|
||||||
return plugin;
|
return plugin;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ n Bridge obfs4 46.226.107.197:10300 A38FD6BDFD902882F5F5B9B7CCC95602A20B0BC4 cer
|
|||||||
n Bridge obfs4 185.181.11.86:443 A961609729E7FDF520B4E81F1F1B8FA1045285C3 cert=e5faG9Zk4Ni+e7z2YgGfevyKPQlMvkVGi4ublSsHYjaBovKeNXpOhbeFxzbZZoAzxAoGUQ iat-mode=0
|
n Bridge obfs4 185.181.11.86:443 A961609729E7FDF520B4E81F1F1B8FA1045285C3 cert=e5faG9Zk4Ni+e7z2YgGfevyKPQlMvkVGi4ublSsHYjaBovKeNXpOhbeFxzbZZoAzxAoGUQ iat-mode=0
|
||||||
n Bridge obfs4 85.242.211.221:8042 A36A938DD7FDB8BACC846BA326EE0BA0D89A9252 cert=1AN6Pt1eFca3Y/WYD2TGAU3Al9cO4eouXE9SX63s66Z/ks3tVmgQ5GeXi1B5DOvx6Il7Zw iat-mode=0
|
n Bridge obfs4 85.242.211.221:8042 A36A938DD7FDB8BACC846BA326EE0BA0D89A9252 cert=1AN6Pt1eFca3Y/WYD2TGAU3Al9cO4eouXE9SX63s66Z/ks3tVmgQ5GeXi1B5DOvx6Il7Zw iat-mode=0
|
||||||
n Bridge obfs4 74.104.165.202:9002 EF432018A6AA5D970B2F84E39CD30A147030141C cert=PhppfUusY85dHGvWtGTybZ1fED4DtbHmALkNMIOIYrAz1B4xN7/2a5gyiZe1epju1BOHVg iat-mode=0
|
n Bridge obfs4 74.104.165.202:9002 EF432018A6AA5D970B2F84E39CD30A147030141C cert=PhppfUusY85dHGvWtGTybZ1fED4DtbHmALkNMIOIYrAz1B4xN7/2a5gyiZe1epju1BOHVg iat-mode=0
|
||||||
n Bridge obfs4 70.34.242.31:443 7F026956402CDFF4BCBA8E11EE9C50E3FE0A2B72 cert=hP/KU7JATSfWH3HwS5Er/YLT0J+bRO3+s2fWx2yirrgf37EyrWvm/BQshoNje8WfUm6CBw iat-mode=0
|
|
||||||
n Bridge obfs4 93.95.226.151:41185 460B0CFFC0CF1D965F3DE064E08BA1915E7C916A cert=inluPzp5Jp5OzZar1eQb4dcQ/YlAj/v0kHAUCoCr3rmLt03+pVuVTjoH4mRy4+acXpn+Gw iat-mode=0
|
n Bridge obfs4 93.95.226.151:41185 460B0CFFC0CF1D965F3DE064E08BA1915E7C916A cert=inluPzp5Jp5OzZar1eQb4dcQ/YlAj/v0kHAUCoCr3rmLt03+pVuVTjoH4mRy4+acXpn+Gw iat-mode=0
|
||||||
n Bridge obfs4 120.29.217.52:5223 40FE3DB9800272F9CDC76422F8ED7883280EE96D cert=/71PS4l8c/XJ4DIItlH9xMqNvPFg2RUTrHvPlQWh48u5et8h/yyyjCcYphUadDsfBWpaGQ iat-mode=0
|
n Bridge obfs4 120.29.217.52:5223 40FE3DB9800272F9CDC76422F8ED7883280EE96D cert=/71PS4l8c/XJ4DIItlH9xMqNvPFg2RUTrHvPlQWh48u5et8h/yyyjCcYphUadDsfBWpaGQ iat-mode=0
|
||||||
n Bridge obfs4 83.97.179.29:1199 D83068BFAA28E71DB024B786E1E803BE14257127 cert=IduGtt05tM59Xmvo0oVNWgIRgY4OGPJjFP+y2oa6RMDHQBL/GRyFOOgX70iiazNAIJNkPw iat-mode=0
|
n Bridge obfs4 83.97.179.29:1199 D83068BFAA28E71DB024B786E1E803BE14257127 cert=IduGtt05tM59Xmvo0oVNWgIRgY4OGPJjFP+y2oa6RMDHQBL/GRyFOOgX70iiazNAIJNkPw iat-mode=0
|
||||||
@@ -24,7 +23,11 @@ n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert
|
|||||||
n Bridge obfs4 65.108.159.114:14174 E1AD374BA9F34BD98862D128AC54D40C7DC138AE cert=YMkxMSBN2OOd99AkJpFaEUyKkdqZgJt9oVJEgO1QnT37n/Vc2yR4nhx4k4VkPLfEP1f4eg iat-mode=0
|
n Bridge obfs4 65.108.159.114:14174 E1AD374BA9F34BD98862D128AC54D40C7DC138AE cert=YMkxMSBN2OOd99AkJpFaEUyKkdqZgJt9oVJEgO1QnT37n/Vc2yR4nhx4k4VkPLfEP1f4eg iat-mode=0
|
||||||
n Bridge obfs4 185.177.207.138:8443 53716FE26F23C8C6A71A2BC5D9D8DC64747278C7 cert=6jcYVilMEzxdsWghSrQFpYYJlkkir/GPIXw/EnddUK3S8ToVpMG8u1SwMOEdEs735RrMHw iat-mode=0
|
n Bridge obfs4 185.177.207.138:8443 53716FE26F23C8C6A71A2BC5D9D8DC64747278C7 cert=6jcYVilMEzxdsWghSrQFpYYJlkkir/GPIXw/EnddUK3S8ToVpMG8u1SwMOEdEs735RrMHw iat-mode=0
|
||||||
n Bridge obfs4 176.123.2.253:1933 B855D141CE6C4DE0F7EA4AAED83EBA8373FA8191 cert=1rOoSaRagc6PPR//paIl+ukv1N+xWKCdBXMFxK0k/moEwH0lk5bURBrUDzIX35fVzaiicQ iat-mode=0
|
n Bridge obfs4 176.123.2.253:1933 B855D141CE6C4DE0F7EA4AAED83EBA8373FA8191 cert=1rOoSaRagc6PPR//paIl+ukv1N+xWKCdBXMFxK0k/moEwH0lk5bURBrUDzIX35fVzaiicQ iat-mode=0
|
||||||
|
n Bridge obfs4 5.252.176.61:9418 3E61130100AD827AB9CB33DAC052D9BC49A39509 cert=/aMyBPnKbQFISithD3i1KHUdpWeMpWG3SvUpq1YWCf2EQohFxQfw+646gW1knm4BI/DLRA iat-mode=0
|
||||||
|
n Bridge obfs4 70.34.213.156:12345 BC1C79ABBAE085D305346E7A2B0E838953B4B4D3 cert=3Sk4uA3/NiAsn4ObOUzjIzARclGmkiEUrku8o8bkq4ZL+dek9uLj/d5LZ5nAXT6L9S0CZA iat-mode=0
|
||||||
|
n Bridge obfs4 202.61.224.111:6902 A4F91299763DB925AE3BD29A0FC1A9821E5D9BAE cert=NBKm2MJ83wMvYShkqpD5RrbDtW5YpIZrFNnMw7Dj1XOM3plU60Bh4eziaQXe8fGtb8ZqKg iat-mode=0
|
||||||
v Bridge 135.181.113.164:54444 74AF4CCA614C454B7D3E81FF8BACD78CEBC7D7DE
|
v Bridge 135.181.113.164:54444 74AF4CCA614C454B7D3E81FF8BACD78CEBC7D7DE
|
||||||
v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F
|
v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F
|
||||||
v Bridge 77.96.91.103:443 ED000A75B79A58F1D83A4D1675C2A9395B71BE8E
|
v Bridge 77.96.91.103:443 ED000A75B79A58F1D83A4D1675C2A9395B71BE8E
|
||||||
|
v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31
|
||||||
m Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
|
m Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
|
||||||
@@ -187,6 +187,32 @@ public class ConnectivityCheckerImplTest extends BrambleMockTestCase {
|
|||||||
checker.onConnectivityCheckSucceeded(now);
|
checker.onConnectivityCheckSucceeded(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckIsCancelledWhenObserverIsRemoved() {
|
||||||
|
ConnectivityCheckerImpl checker = createChecker();
|
||||||
|
|
||||||
|
// When checkConnectivity() is called a check should be started
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(now));
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(apiCall);
|
||||||
|
will(returnValue(task));
|
||||||
|
}});
|
||||||
|
|
||||||
|
checker.checkConnectivity(properties, observer1);
|
||||||
|
|
||||||
|
// When the observer is removed the check should be cancelled
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(task).cancel();
|
||||||
|
}});
|
||||||
|
|
||||||
|
checker.removeObserver(observer1);
|
||||||
|
|
||||||
|
// If the check runs anyway (cancellation came too late) the observer
|
||||||
|
// should not be called
|
||||||
|
checker.onConnectivityCheckSucceeded(now);
|
||||||
|
}
|
||||||
|
|
||||||
private ConnectivityCheckerImpl createChecker() {
|
private ConnectivityCheckerImpl createChecker() {
|
||||||
|
|
||||||
return new ConnectivityCheckerImpl(clock, mailboxApiCaller) {
|
return new ConnectivityCheckerImpl(clock, mailboxApiCaller) {
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||||
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
public class ContactMailboxDownloadWorkerTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
private final ConnectivityChecker connectivityChecker =
|
||||||
|
context.mock(ConnectivityChecker.class);
|
||||||
|
private final TorReachabilityMonitor torReachabilityMonitor =
|
||||||
|
context.mock(TorReachabilityMonitor.class);
|
||||||
|
private final MailboxApiCaller mailboxApiCaller =
|
||||||
|
context.mock(MailboxApiCaller.class);
|
||||||
|
private final MailboxApi mailboxApi = context.mock(MailboxApi.class);
|
||||||
|
private final MailboxFileManager mailboxFileManager =
|
||||||
|
context.mock(MailboxFileManager.class);
|
||||||
|
|
||||||
|
private final MailboxProperties mailboxProperties =
|
||||||
|
getMailboxProperties(false, CLIENT_SUPPORTS);
|
||||||
|
private final long now = System.currentTimeMillis();
|
||||||
|
private final MailboxFile file1 =
|
||||||
|
new MailboxFile(new MailboxFileId(getRandomId()), now - 1);
|
||||||
|
private final MailboxFile file2 =
|
||||||
|
new MailboxFile(new MailboxFileId(getRandomId()), now);
|
||||||
|
private final List<MailboxFile> files = asList(file1, file2);
|
||||||
|
|
||||||
|
private File testDir, tempFile;
|
||||||
|
private ContactMailboxDownloadWorker worker;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
testDir = getTestDirectory();
|
||||||
|
tempFile = new File(testDir, "temp");
|
||||||
|
worker = new ContactMailboxDownloadWorker(connectivityChecker,
|
||||||
|
torReachabilityMonitor, mailboxApiCaller, mailboxApi,
|
||||||
|
mailboxFileManager, mailboxProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
deleteTestDirectory(testDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChecksConnectivityWhenStartedAndRemovesObserverWhenDestroyed() {
|
||||||
|
// When the worker is started it should start a connectivity check
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(connectivityChecker).checkConnectivity(
|
||||||
|
with(mailboxProperties), with(worker));
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.start();
|
||||||
|
|
||||||
|
// When the worker is destroyed it should remove the connectivity
|
||||||
|
// and reachability observers
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(connectivityChecker).removeObserver(worker);
|
||||||
|
oneOf(torReachabilityMonitor).removeObserver(worker);
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDownloadsFilesWhenConnectivityCheckSucceeds()
|
||||||
|
throws Exception {
|
||||||
|
// When the worker is started it should start a connectivity check
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(connectivityChecker).checkConnectivity(
|
||||||
|
with(mailboxProperties), with(worker));
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.start();
|
||||||
|
|
||||||
|
// When the connectivity check succeeds, a list-inbox task should be
|
||||||
|
// started for the first download cycle
|
||||||
|
AtomicReference<ApiCall> listTask = new AtomicReference<>(null);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(listTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.onConnectivityCheckSucceeded();
|
||||||
|
|
||||||
|
// When the list-inbox tasks runs and finds some files to download,
|
||||||
|
// it should start a download task for the first file
|
||||||
|
AtomicReference<ApiCall> downloadTask = new AtomicReference<>(null);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApi).getFiles(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()));
|
||||||
|
will(returnValue(files));
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(downloadTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(listTask.get().callApi());
|
||||||
|
|
||||||
|
// When the first download task runs it should download the file to the
|
||||||
|
// location provided by the file manager and start a delete task
|
||||||
|
AtomicReference<ApiCall> deleteTask = new AtomicReference<>(null);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxFileManager).createTempFileForDownload();
|
||||||
|
will(returnValue(tempFile));
|
||||||
|
oneOf(mailboxApi).getFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()),
|
||||||
|
file1.name, tempFile);
|
||||||
|
oneOf(mailboxFileManager).handleDownloadedFile(tempFile);
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(deleteTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(downloadTask.get().callApi());
|
||||||
|
|
||||||
|
// When the first delete task runs it should delete the file, ignore
|
||||||
|
// the tolerable failure, and start a download task for the next file
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApi).deleteFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()), file1.name);
|
||||||
|
will(throwException(new TolerableFailureException()));
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(downloadTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(deleteTask.get().callApi());
|
||||||
|
|
||||||
|
// When the second download task runs it should download the file to
|
||||||
|
// the location provided by the file manager and start a delete task
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxFileManager).createTempFileForDownload();
|
||||||
|
will(returnValue(tempFile));
|
||||||
|
oneOf(mailboxApi).getFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()),
|
||||||
|
file2.name, tempFile);
|
||||||
|
oneOf(mailboxFileManager).handleDownloadedFile(tempFile);
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(deleteTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(downloadTask.get().callApi());
|
||||||
|
|
||||||
|
// When the second delete task runs it should delete the file and
|
||||||
|
// start a list-inbox task to check for files that may have arrived
|
||||||
|
// since the first download cycle started
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApi).deleteFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()), file2.name);
|
||||||
|
will(throwException(new TolerableFailureException()));
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(listTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(deleteTask.get().callApi());
|
||||||
|
|
||||||
|
// When the list-inbox tasks runs and finds no more files to download,
|
||||||
|
// it should add a Tor reachability observer
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApi).getFiles(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()));
|
||||||
|
will(returnValue(emptyList()));
|
||||||
|
oneOf(torReachabilityMonitor).addOneShotObserver(worker);
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(listTask.get().callApi());
|
||||||
|
|
||||||
|
// When the reachability observer is called, a list-inbox task should
|
||||||
|
// be started for the second download cycle
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(listTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.onTorReachable();
|
||||||
|
|
||||||
|
// When the list-inbox tasks runs and finds no more files to download,
|
||||||
|
// it should finish the second download cycle
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApi).getFiles(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()));
|
||||||
|
will(returnValue(emptyList()));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(listTask.get().callApi());
|
||||||
|
|
||||||
|
// When the worker is destroyed it should remove the connectivity
|
||||||
|
// and reachability observers
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(connectivityChecker).removeObserver(worker);
|
||||||
|
oneOf(torReachabilityMonitor).removeObserver(worker);
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,45 +1,69 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.PollingIntervalDecreasedEvent;
|
||||||
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.jmock.Expectations;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class BackoffImplTest extends BrambleTestCase {
|
public class BackoffImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
private final EventBus eventBus = context.mock(EventBus.class);
|
||||||
|
|
||||||
|
private final TransportId transportId = getTransportId();
|
||||||
private static final int MIN_INTERVAL = 60 * 1000;
|
private static final int MIN_INTERVAL = 60 * 1000;
|
||||||
private static final int MAX_INTERVAL = 60 * 60 * 1000;
|
private static final int MAX_INTERVAL = 60 * 60 * 1000;
|
||||||
private static final double BASE = 1.2;
|
private static final double BASE = 1.2;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPollingIntervalStartsAtMinimum() {
|
public void testPollingIntervalStartsAtMinimum() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
|
MIN_INTERVAL, MAX_INTERVAL, BASE);
|
||||||
assertEquals(MIN_INTERVAL, b.getPollingInterval());
|
assertEquals(MIN_INTERVAL, b.getPollingInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIncrementIncreasesPollingInterval() {
|
public void testIncrementMethodIncreasesPollingInterval() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
|
MIN_INTERVAL, MAX_INTERVAL, BASE);
|
||||||
b.increment();
|
b.increment();
|
||||||
assertTrue(b.getPollingInterval() > MIN_INTERVAL);
|
assertTrue(b.getPollingInterval() > MIN_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResetResetsPollingInterval() {
|
public void testResetMethodResetsPollingInterval() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
b.increment();
|
MIN_INTERVAL, MAX_INTERVAL, BASE);
|
||||||
b.increment();
|
b.increment();
|
||||||
|
assertTrue(b.getPollingInterval() > MIN_INTERVAL);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).broadcast(with(any(
|
||||||
|
PollingIntervalDecreasedEvent.class)));
|
||||||
|
}});
|
||||||
|
|
||||||
|
b.reset();
|
||||||
|
assertEquals(MIN_INTERVAL, b.getPollingInterval());
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Resetting again should not broadcast another event
|
||||||
b.reset();
|
b.reset();
|
||||||
assertEquals(MIN_INTERVAL, b.getPollingInterval());
|
assertEquals(MIN_INTERVAL, b.getPollingInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBaseAffectsBackoffSpeed() {
|
public void testBaseAffectsBackoffSpeed() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
|
MIN_INTERVAL, MAX_INTERVAL, BASE);
|
||||||
b.increment();
|
b.increment();
|
||||||
int interval = b.getPollingInterval();
|
int interval = b.getPollingInterval();
|
||||||
BackoffImpl b1 = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE * 2);
|
BackoffImpl b1 = new BackoffImpl(eventBus, transportId, MIN_INTERVAL,
|
||||||
|
MAX_INTERVAL, BASE * 2);
|
||||||
b1.increment();
|
b1.increment();
|
||||||
int interval1 = b1.getPollingInterval();
|
int interval1 = b1.getPollingInterval();
|
||||||
assertTrue(interval < interval1);
|
assertTrue(interval < interval1);
|
||||||
@@ -47,15 +71,16 @@ public class BackoffImplTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIntervalDoesNotExceedMaxInterval() {
|
public void testIntervalDoesNotExceedMaxInterval() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
|
MIN_INTERVAL, MAX_INTERVAL, BASE);
|
||||||
for (int i = 0; i < 100; i++) b.increment();
|
for (int i = 0; i < 100; i++) b.increment();
|
||||||
assertEquals(MAX_INTERVAL, b.getPollingInterval());
|
assertEquals(MAX_INTERVAL, b.getPollingInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIntervalDoesNotExceedMaxIntervalWithInfiniteMultiplier() {
|
public void testIntervalDoesNotExceedMaxIntervalWithInfiniteMultiplier() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL,
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
Double.POSITIVE_INFINITY);
|
MIN_INTERVAL, MAX_INTERVAL, Double.POSITIVE_INFINITY);
|
||||||
b.increment();
|
b.increment();
|
||||||
assertEquals(MAX_INTERVAL, b.getPollingInterval());
|
assertEquals(MAX_INTERVAL, b.getPollingInterval());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
import org.briarproject.bramble.api.plugin.event.PollingIntervalDecreasedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
||||||
@@ -35,7 +35,6 @@ import java.util.concurrent.Executor;
|
|||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.Collections.singletonMap;
|
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.CollectionMatcher.collectionOf;
|
import static org.briarproject.bramble.test.CollectionMatcher.collectionOf;
|
||||||
@@ -217,7 +216,7 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRescheduleOnConnectionOpened() {
|
public void testRescheduleOnPollingIntervalDecreased() {
|
||||||
Plugin plugin = context.mock(Plugin.class);
|
Plugin plugin = context.mock(Plugin.class);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
@@ -240,8 +239,7 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(cancellable));
|
will(returnValue(cancellable));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
|
poller.eventOccurred(new PollingIntervalDecreasedEvent(transportId));
|
||||||
false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -281,10 +279,8 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(now + 1));
|
will(returnValue(now + 1));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
|
poller.eventOccurred(new PollingIntervalDecreasedEvent(transportId));
|
||||||
false));
|
poller.eventOccurred(new PollingIntervalDecreasedEvent(transportId));
|
||||||
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
|
|
||||||
false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -328,10 +324,8 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
with(MILLISECONDS));
|
with(MILLISECONDS));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
|
poller.eventOccurred(new PollingIntervalDecreasedEvent(transportId));
|
||||||
false));
|
poller.eventOccurred(new PollingIntervalDecreasedEvent(transportId));
|
||||||
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
|
|
||||||
false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -378,48 +372,6 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
poller.eventOccurred(new TransportActiveEvent(transportId));
|
poller.eventOccurred(new TransportActiveEvent(transportId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
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(ioExecutor), with(0L), with(MILLISECONDS));
|
|
||||||
will(returnValue(cancellable));
|
|
||||||
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(ioExecutor), with((long) (pollingInterval * 0.5)),
|
|
||||||
with(MILLISECONDS));
|
|
||||||
will(returnValue(cancellable));
|
|
||||||
// Get the transport properties and connected contacts
|
|
||||||
oneOf(transportPropertyManager).getRemoteProperties(transportId);
|
|
||||||
will(returnValue(singletonMap(contactId, properties)));
|
|
||||||
oneOf(connectionRegistry).getConnectedOrBetterContacts(transportId);
|
|
||||||
will(returnValue(singletonList(contactId)));
|
|
||||||
// All contacts are connected, so don't poll the plugin
|
|
||||||
}});
|
|
||||||
|
|
||||||
poller.eventOccurred(new TransportActiveEvent(transportId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCancelsPollingOnTransportDeactivated() {
|
public void testCancelsPollingOnTransportDeactivated() {
|
||||||
Plugin plugin = context.mock(Plugin.class);
|
Plugin plugin = context.mock(Plugin.class);
|
||||||
|
|||||||
@@ -342,6 +342,10 @@ public class LanTcpPluginTest extends BrambleTestCase {
|
|||||||
public void pluginStateChanged(State newState) {
|
public void pluginStateChanged(State newState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pollingIntervalDecreased() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleConnection(DuplexTransportConnection d) {
|
public void handleConnection(DuplexTransportConnection d) {
|
||||||
connectionsLatch.countDown();
|
connectionsLatch.countDown();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import java.util.HashSet;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BLOCKED;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BLOCKED;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BRIDGES;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BRIDGES;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
||||||
@@ -15,7 +14,7 @@ import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeTy
|
|||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DEFAULT_BRIDGES;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DEFAULT_BRIDGES;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.MEEK_BRIDGES;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DPI_BRIDGES;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.NON_DEFAULT_BRIDGES;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.NON_DEFAULT_BRIDGES;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -32,18 +31,18 @@ public class CircumventionProviderTest extends BrambleTestCase {
|
|||||||
Set<String> defaultBridges = new HashSet<>(asList(DEFAULT_BRIDGES));
|
Set<String> defaultBridges = new HashSet<>(asList(DEFAULT_BRIDGES));
|
||||||
Set<String> nonDefaultBridges =
|
Set<String> nonDefaultBridges =
|
||||||
new HashSet<>(asList(NON_DEFAULT_BRIDGES));
|
new HashSet<>(asList(NON_DEFAULT_BRIDGES));
|
||||||
Set<String> meekBridges = new HashSet<>(asList(MEEK_BRIDGES));
|
Set<String> dpiBridges = new HashSet<>(asList(DPI_BRIDGES));
|
||||||
// BRIDGES should be a subset of BLOCKED
|
// BRIDGES should be a subset of BLOCKED
|
||||||
assertTrue(blocked.containsAll(bridges));
|
assertTrue(blocked.containsAll(bridges));
|
||||||
// BRIDGES should be the union of the bridge type sets
|
// BRIDGES should be the union of the bridge type sets
|
||||||
Set<String> union = new HashSet<>(defaultBridges);
|
Set<String> union = new HashSet<>(defaultBridges);
|
||||||
union.addAll(nonDefaultBridges);
|
union.addAll(nonDefaultBridges);
|
||||||
union.addAll(meekBridges);
|
union.addAll(dpiBridges);
|
||||||
assertEquals(bridges, union);
|
assertEquals(bridges, union);
|
||||||
// The bridge type sets should not overlap
|
// The bridge type sets should not overlap
|
||||||
assertEmptyIntersection(defaultBridges, nonDefaultBridges);
|
assertEmptyIntersection(defaultBridges, nonDefaultBridges);
|
||||||
assertEmptyIntersection(defaultBridges, meekBridges);
|
assertEmptyIntersection(defaultBridges, dpiBridges);
|
||||||
assertEmptyIntersection(nonDefaultBridges, meekBridges);
|
assertEmptyIntersection(nonDefaultBridges, dpiBridges);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -56,8 +55,8 @@ public class CircumventionProviderTest extends BrambleTestCase {
|
|||||||
assertEquals(asList(NON_DEFAULT_OBFS4, VANILLA),
|
assertEquals(asList(NON_DEFAULT_OBFS4, VANILLA),
|
||||||
provider.getSuitableBridgeTypes(country));
|
provider.getSuitableBridgeTypes(country));
|
||||||
}
|
}
|
||||||
for (String country : MEEK_BRIDGES) {
|
for (String country : DPI_BRIDGES) {
|
||||||
assertEquals(singletonList(MEEK),
|
assertEquals(asList(NON_DEFAULT_OBFS4, MEEK),
|
||||||
provider.getSuitableBridgeTypes(country));
|
provider.getSuitableBridgeTypes(country));
|
||||||
}
|
}
|
||||||
assertEquals(asList(DEFAULT_OBFS4, VANILLA),
|
assertEquals(asList(DEFAULT_OBFS4, VANILLA),
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ dependencies {
|
|||||||
implementation "net.java.dev.jna:jna:$jna_version"
|
implementation "net.java.dev.jna:jna:$jna_version"
|
||||||
implementation "net.java.dev.jna:jna-platform:$jna_version"
|
implementation "net.java.dev.jna:jna-platform:$jna_version"
|
||||||
tor "org.briarproject:tor-linux:$tor_version"
|
tor "org.briarproject:tor-linux:$tor_version"
|
||||||
|
tor "org.briarproject:tor-windows:$tor_version"
|
||||||
tor "org.briarproject:obfs4proxy-linux:$obfs4proxy_version"
|
tor "org.briarproject:obfs4proxy-linux:$obfs4proxy_version"
|
||||||
|
tor "org.briarproject:obfs4proxy-windows:$obfs4proxy_version"
|
||||||
|
|
||||||
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
|
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
|
|||||||
BluetoothConnectionFactory<StreamConnection> connectionFactory =
|
BluetoothConnectionFactory<StreamConnection> connectionFactory =
|
||||||
new JavaBluetoothConnectionFactory(connectionLimiter,
|
new JavaBluetoothConnectionFactory(connectionLimiter,
|
||||||
timeoutMonitor);
|
timeoutMonitor);
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(eventBus, ID,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
|
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
|
||||||
connectionFactory, ioExecutor, wakefulIoExecutor, secureRandom,
|
connectionFactory, ioExecutor, wakefulIoExecutor, secureRandom,
|
||||||
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
|
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.tor;
|
|||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
@@ -29,7 +28,6 @@ abstract class JavaTorPlugin extends TorPlugin {
|
|||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
TorRendezvousCrypto torRendezvousCrypto,
|
||||||
PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture,
|
String architecture,
|
||||||
@@ -40,7 +38,7 @@ abstract class JavaTorPlugin extends TorPlugin {
|
|||||||
int torControlPort) {
|
int torControlPort) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
torSocketFactory, clock, resourceProvider,
|
torSocketFactory, clock, resourceProvider,
|
||||||
circumventionProvider, batteryManager, backoff,
|
circumventionProvider, batteryManager,
|
||||||
torRendezvousCrypto, callback, architecture,
|
torRendezvousCrypto, callback, architecture,
|
||||||
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
||||||
torControlPort);
|
torControlPort);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.sun.jna.Native;
|
|||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
@@ -29,7 +28,6 @@ class UnixTorPlugin extends JavaTorPlugin {
|
|||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
TorRendezvousCrypto torRendezvousCrypto,
|
||||||
PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture,
|
String architecture,
|
||||||
@@ -40,7 +38,7 @@ class UnixTorPlugin extends JavaTorPlugin {
|
|||||||
int torControlPort) {
|
int torControlPort) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
torSocketFactory, clock, resourceProvider,
|
torSocketFactory, clock, resourceProvider,
|
||||||
circumventionProvider, batteryManager, backoff,
|
circumventionProvider, batteryManager,
|
||||||
torRendezvousCrypto, callback, architecture,
|
torRendezvousCrypto, callback, architecture,
|
||||||
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
||||||
torControlPort);
|
torControlPort);
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
@@ -39,7 +37,6 @@ public class UnixTorPluginFactory extends TorPluginFactory {
|
|||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory,
|
|
||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
@@ -49,7 +46,7 @@ public class UnixTorPluginFactory extends TorPluginFactory {
|
|||||||
@TorSocksPort int torSocksPort,
|
@TorSocksPort int torSocksPort,
|
||||||
@TorControlPort int torControlPort) {
|
@TorControlPort int torControlPort) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
eventBus, torSocketFactory, backoffFactory, resourceProvider,
|
eventBus, torSocketFactory, resourceProvider,
|
||||||
circumventionProvider, batteryManager, clock, crypto,
|
circumventionProvider, batteryManager, clock, crypto,
|
||||||
torDirectory, torSocksPort, torControlPort);
|
torDirectory, torSocksPort, torControlPort);
|
||||||
}
|
}
|
||||||
@@ -69,13 +66,13 @@ public class UnixTorPluginFactory extends TorPluginFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
TorPlugin createPluginInstance(Backoff backoff,
|
TorPlugin createPluginInstance(TorRendezvousCrypto torRendezvousCrypto,
|
||||||
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture) {
|
String architecture) {
|
||||||
return new UnixTorPlugin(ioExecutor, wakefulIoExecutor,
|
return new UnixTorPlugin(ioExecutor, wakefulIoExecutor,
|
||||||
networkManager, locationUtils, torSocketFactory, clock,
|
networkManager, locationUtils, torSocketFactory, clock,
|
||||||
resourceProvider, circumventionProvider, batteryManager,
|
resourceProvider, circumventionProvider, batteryManager,
|
||||||
backoff, torRendezvousCrypto, callback, architecture,
|
torRendezvousCrypto, callback, architecture,
|
||||||
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
||||||
torControlPort);
|
torControlPort);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package org.briarproject.bramble.plugin.tor;
|
||||||
|
|
||||||
|
import com.sun.jna.platform.win32.Kernel32;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginException;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class WindowsTorPlugin extends JavaTorPlugin {
|
||||||
|
|
||||||
|
WindowsTorPlugin(Executor ioExecutor,
|
||||||
|
Executor wakefulIoExecutor,
|
||||||
|
NetworkManager networkManager,
|
||||||
|
LocationUtils locationUtils,
|
||||||
|
SocketFactory torSocketFactory,
|
||||||
|
Clock clock,
|
||||||
|
ResourceProvider resourceProvider,
|
||||||
|
CircumventionProvider circumventionProvider,
|
||||||
|
BatteryManager batteryManager,
|
||||||
|
TorRendezvousCrypto torRendezvousCrypto,
|
||||||
|
PluginCallback callback,
|
||||||
|
String architecture,
|
||||||
|
long maxLatency,
|
||||||
|
int maxIdleTime,
|
||||||
|
File torDirectory,
|
||||||
|
int torSocksPort,
|
||||||
|
int torControlPort) {
|
||||||
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
|
torSocketFactory, clock, resourceProvider,
|
||||||
|
circumventionProvider, batteryManager,
|
||||||
|
torRendezvousCrypto, callback, architecture,
|
||||||
|
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
||||||
|
torControlPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getProcessId() {
|
||||||
|
return Kernel32.INSTANCE.GetCurrentProcessId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void waitForTorToStart(Process torProcess)
|
||||||
|
throws InterruptedException, PluginException {
|
||||||
|
// On Windows the RunAsDaemon option has no effect, so Tor won't detach.
|
||||||
|
// Wait for the control port to be opened, then continue to read its
|
||||||
|
// stdout and stderr in a background thread until it exits.
|
||||||
|
BlockingQueue<Boolean> success = new ArrayBlockingQueue<>(1);
|
||||||
|
ioExecutor.execute(() -> {
|
||||||
|
boolean started = false;
|
||||||
|
// Read the process's stdout (and redirected stderr)
|
||||||
|
Scanner stdout = new Scanner(torProcess.getInputStream());
|
||||||
|
// Log the first line of stdout (contains Tor and library versions)
|
||||||
|
if (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
||||||
|
// Startup has succeeded when the control port is open
|
||||||
|
while (stdout.hasNextLine()) {
|
||||||
|
String line = stdout.nextLine();
|
||||||
|
if (!started && line.contains("Opened Control listener")) {
|
||||||
|
success.add(true);
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stdout.close();
|
||||||
|
// If the control port wasn't opened, startup has failed
|
||||||
|
if (!started) success.add(false);
|
||||||
|
// Wait for the process to exit
|
||||||
|
try {
|
||||||
|
int exit = torProcess.waitFor();
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Tor exited with value " + exit);
|
||||||
|
} catch (InterruptedException e1) {
|
||||||
|
LOG.warning("Interrupted while waiting for Tor to exit");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Wait for the startup result
|
||||||
|
if (!success.take()) throw new PluginException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package org.briarproject.bramble.plugin.tor;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
|
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||||
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
|
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static org.briarproject.bramble.util.OsUtils.isWindows;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class WindowsTorPluginFactory extends TorPluginFactory {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
WindowsTorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||||
|
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||||
|
NetworkManager networkManager,
|
||||||
|
LocationUtils locationUtils,
|
||||||
|
EventBus eventBus,
|
||||||
|
SocketFactory torSocketFactory,
|
||||||
|
ResourceProvider resourceProvider,
|
||||||
|
CircumventionProvider circumventionProvider,
|
||||||
|
BatteryManager batteryManager,
|
||||||
|
Clock clock,
|
||||||
|
CryptoComponent crypto,
|
||||||
|
@TorDirectory File torDirectory,
|
||||||
|
@TorSocksPort int torSocksPort,
|
||||||
|
@TorControlPort int torControlPort) {
|
||||||
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
|
eventBus, torSocketFactory, resourceProvider,
|
||||||
|
circumventionProvider, batteryManager, clock, crypto,
|
||||||
|
torDirectory, torSocksPort, torControlPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
String getArchitectureForTorBinary() {
|
||||||
|
if (!isWindows()) return null;
|
||||||
|
String arch = System.getProperty("os.arch");
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("System's os.arch is " + arch);
|
||||||
|
}
|
||||||
|
if (arch.equals("amd64")) return "windows-x86_64";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
TorPlugin createPluginInstance(TorRendezvousCrypto torRendezvousCrypto,
|
||||||
|
PluginCallback callback,
|
||||||
|
String architecture) {
|
||||||
|
return new WindowsTorPlugin(ioExecutor, wakefulIoExecutor,
|
||||||
|
networkManager, locationUtils, torSocketFactory, clock,
|
||||||
|
resourceProvider, circumventionProvider, batteryManager,
|
||||||
|
torRendezvousCrypto, callback, architecture,
|
||||||
|
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
||||||
|
torControlPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,9 +16,7 @@ public class DesktopSecureRandomModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
SecureRandomProvider provideSecureRandomProvider() {
|
SecureRandomProvider provideSecureRandomProvider() {
|
||||||
if (isLinux() || isMac())
|
if (isLinux() || isMac()) return new UnixSecureRandomProvider();
|
||||||
return new UnixSecureRandomProvider();
|
return () -> null; // Use system default
|
||||||
// TODO: Create a secure random provider for Windows
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
@@ -110,8 +109,6 @@ public class BridgeTest extends BrambleTestCase {
|
|||||||
@Inject
|
@Inject
|
||||||
EventBus eventBus;
|
EventBus eventBus;
|
||||||
@Inject
|
@Inject
|
||||||
BackoffFactory backoffFactory;
|
|
||||||
@Inject
|
|
||||||
Clock clock;
|
Clock clock;
|
||||||
@Inject
|
@Inject
|
||||||
CryptoComponent crypto;
|
CryptoComponent crypto;
|
||||||
@@ -166,9 +163,8 @@ public class BridgeTest extends BrambleTestCase {
|
|||||||
};
|
};
|
||||||
factory = new UnixTorPluginFactory(ioExecutor, wakefulIoExecutor,
|
factory = new UnixTorPluginFactory(ioExecutor, wakefulIoExecutor,
|
||||||
networkManager, locationUtils, eventBus, torSocketFactory,
|
networkManager, locationUtils, eventBus, torSocketFactory,
|
||||||
backoffFactory, resourceProvider, bridgeProvider,
|
resourceProvider, bridgeProvider, batteryManager, clock,
|
||||||
batteryManager, clock, crypto, torDir,
|
crypto, torDir, SOCKS_PORT, CONTROL_PORT);
|
||||||
SOCKS_PORT, CONTROL_PORT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ public class TestPluginCallback implements PluginCallback {
|
|||||||
public void pluginStateChanged(State state) {
|
public void pluginStateChanged(State state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pollingIntervalDecreased() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleConnection(DuplexTransportConnection c) {
|
public void handleConnection(DuplexTransportConnection c) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ dependencyVerification {
|
|||||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||||
'org.briarproject:obfs4proxy-linux:0.0.12:obfs4proxy-linux-0.0.12.jar:3dd83aff25fe1cb3e4eab78a02c76ac921f552be6877b3af83a472438525df2a',
|
'org.briarproject:obfs4proxy-linux:0.0.12:obfs4proxy-linux-0.0.12.jar:3dd83aff25fe1cb3e4eab78a02c76ac921f552be6877b3af83a472438525df2a',
|
||||||
|
'org.briarproject:obfs4proxy-windows:0.0.12:obfs4proxy-windows-0.0.12.jar:392aa4b9d9c6fef0c659c4068d019d6c6471991bbb62ff00553884ec36018c7b',
|
||||||
'org.briarproject:tor-linux:0.4.5.12-2:tor-linux-0.4.5.12-2.jar:d275f323faf5e70b33d2c8a1bdab1bb3ab5a0d8f4e23c4a6dda03d86f4e95838',
|
'org.briarproject:tor-linux:0.4.5.12-2:tor-linux-0.4.5.12-2.jar:d275f323faf5e70b33d2c8a1bdab1bb3ab5a0d8f4e23c4a6dda03d86f4e95838',
|
||||||
|
'org.briarproject:tor-windows:0.4.5.12-2:tor-windows-0.4.5.12-2.jar:46599a15d099ed35a360113293f66acc119571c24ec2e37e85e4fb54b4722e07',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
||||||
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||||
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
||||||
|
|||||||
@@ -59,7 +59,12 @@ void jarFactory(Jar jarTask, jarArchitecture) {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
it.duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
|
it.duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
|
||||||
String[] architectures = ["linux-aarch64", "linux-armhf", "linux-x86_64"]
|
String[] architectures = [
|
||||||
|
"linux-aarch64",
|
||||||
|
"linux-armhf",
|
||||||
|
"linux-x86_64",
|
||||||
|
"windows-x86_64"
|
||||||
|
]
|
||||||
for (String arch : architectures) {
|
for (String arch : architectures) {
|
||||||
if (arch != jarArchitecture) {
|
if (arch != jarArchitecture) {
|
||||||
exclude "obfs4proxy_" + arch + ".zip"
|
exclude "obfs4proxy_" + arch + ".zip"
|
||||||
@@ -112,6 +117,10 @@ task x86LinuxJar(type: Jar) {
|
|||||||
jarFactory(it, 'linux-x86_64')
|
jarFactory(it, 'linux-x86_64')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task windowsJar(type: Jar) {
|
||||||
|
jarFactory(it, 'windows-x86_64')
|
||||||
|
}
|
||||||
|
|
||||||
task linuxJars {
|
task linuxJars {
|
||||||
dependsOn(aarch64LinuxJar, armhfLinuxJar, x86LinuxJar)
|
dependsOn(aarch64LinuxJar, armhfLinuxJar, x86LinuxJar)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import org.briarproject.bramble.event.DefaultEventExecutorModule
|
|||||||
import org.briarproject.bramble.network.JavaNetworkModule
|
import org.briarproject.bramble.network.JavaNetworkModule
|
||||||
import org.briarproject.bramble.plugin.tor.CircumventionModule
|
import org.briarproject.bramble.plugin.tor.CircumventionModule
|
||||||
import org.briarproject.bramble.plugin.tor.UnixTorPluginFactory
|
import org.briarproject.bramble.plugin.tor.UnixTorPluginFactory
|
||||||
|
import org.briarproject.bramble.plugin.tor.WindowsTorPluginFactory
|
||||||
import org.briarproject.bramble.socks.SocksModule
|
import org.briarproject.bramble.socks.SocksModule
|
||||||
import org.briarproject.bramble.system.ClockModule
|
import org.briarproject.bramble.system.ClockModule
|
||||||
import org.briarproject.bramble.system.DefaultTaskSchedulerModule
|
import org.briarproject.bramble.system.DefaultTaskSchedulerModule
|
||||||
@@ -29,6 +30,7 @@ import org.briarproject.bramble.system.DesktopSecureRandomModule
|
|||||||
import org.briarproject.bramble.system.JavaSystemModule
|
import org.briarproject.bramble.system.JavaSystemModule
|
||||||
import org.briarproject.bramble.util.OsUtils.isLinux
|
import org.briarproject.bramble.util.OsUtils.isLinux
|
||||||
import org.briarproject.bramble.util.OsUtils.isMac
|
import org.briarproject.bramble.util.OsUtils.isMac
|
||||||
|
import org.briarproject.bramble.util.OsUtils.isWindows
|
||||||
import org.briarproject.briar.headless.blogs.HeadlessBlogModule
|
import org.briarproject.briar.headless.blogs.HeadlessBlogModule
|
||||||
import org.briarproject.briar.headless.contact.HeadlessContactModule
|
import org.briarproject.briar.headless.contact.HeadlessContactModule
|
||||||
import org.briarproject.briar.headless.event.HeadlessEventModule
|
import org.briarproject.briar.headless.event.HeadlessEventModule
|
||||||
@@ -94,9 +96,15 @@ internal class HeadlessModule(private val appDir: File) {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
internal fun providePluginConfig(tor: UnixTorPluginFactory): PluginConfig {
|
internal fun providePluginConfig(
|
||||||
val duplex: List<DuplexPluginFactory> =
|
unixTor: UnixTorPluginFactory,
|
||||||
if (isLinux() || isMac()) listOf(tor) else emptyList()
|
winTor: WindowsTorPluginFactory
|
||||||
|
): PluginConfig {
|
||||||
|
val duplex: List<DuplexPluginFactory> = when {
|
||||||
|
isLinux() || isMac() -> listOf(unixTor)
|
||||||
|
isWindows() -> listOf(winTor)
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
return object : PluginConfig {
|
return object : PluginConfig {
|
||||||
override fun getDuplexFactories(): Collection<DuplexPluginFactory> = duplex
|
override fun getDuplexFactories(): Collection<DuplexPluginFactory> = duplex
|
||||||
override fun getSimplexFactories(): Collection<SimplexPluginFactory> = emptyList()
|
override fun getSimplexFactories(): Collection<SimplexPluginFactory> = emptyList()
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import com.github.ajalt.clikt.parameters.options.option
|
|||||||
import com.github.ajalt.clikt.parameters.types.int
|
import com.github.ajalt.clikt.parameters.types.int
|
||||||
import org.bouncycastle.util.encoders.Base64.toBase64String
|
import org.bouncycastle.util.encoders.Base64.toBase64String
|
||||||
import org.briarproject.bramble.BrambleCoreEagerSingletons
|
import org.briarproject.bramble.BrambleCoreEagerSingletons
|
||||||
|
import org.briarproject.bramble.util.OsUtils.isLinux
|
||||||
|
import org.briarproject.bramble.util.OsUtils.isMac
|
||||||
import org.briarproject.briar.BriarCoreEagerSingletons
|
import org.briarproject.briar.BriarCoreEagerSingletons
|
||||||
import org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY
|
import org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -90,11 +92,13 @@ private class Main : CliktCommand(
|
|||||||
} else if (!file.isDirectory) {
|
} else if (!file.isDirectory) {
|
||||||
throw IOException("Data dir is not a directory: ${file.absolutePath}")
|
throw IOException("Data dir is not a directory: ${file.absolutePath}")
|
||||||
}
|
}
|
||||||
val perms = HashSet<PosixFilePermission>()
|
if (isLinux() || isMac()) {
|
||||||
perms.add(OWNER_READ)
|
val perms = HashSet<PosixFilePermission>()
|
||||||
perms.add(OWNER_WRITE)
|
perms.add(OWNER_READ)
|
||||||
perms.add(OWNER_EXECUTE)
|
perms.add(OWNER_WRITE)
|
||||||
setPosixFilePermissions(file.toPath(), perms)
|
perms.add(OWNER_EXECUTE)
|
||||||
|
setPosixFilePermissions(file.toPath(), perms)
|
||||||
|
}
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user