Use exponential backoff for Tor polling. #314

This commit is contained in:
akwizgran
2016-05-04 12:05:37 +01:00
parent dd9bc74262
commit fbb3c0f9f2
3 changed files with 40 additions and 56 deletions

View File

@@ -38,7 +38,7 @@ public class AndroidPluginsModule {
DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor, DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor,
androidExecutor, appContext, random, backoffFactory); androidExecutor, appContext, random, backoffFactory);
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext, DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext,
locationUtils, reporter, eventBus); locationUtils, reporter, eventBus, backoffFactory);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor, DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
backoffFactory, appContext); backoffFactory, appContext);
final Collection<DuplexPluginFactory> duplex = final Collection<DuplexPluginFactory> duplex =

View File

@@ -23,13 +23,13 @@ import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.SettingsUpdatedEvent; import org.briarproject.api.event.SettingsUpdatedEvent;
import org.briarproject.api.keyagreement.KeyAgreementListener; import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.TransportDescriptor; import org.briarproject.api.keyagreement.TransportDescriptor;
import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import org.briarproject.api.properties.TransportProperties; import org.briarproject.api.properties.TransportProperties;
import org.briarproject.api.reporting.DevReporter; import org.briarproject.api.reporting.DevReporter;
import org.briarproject.api.settings.Settings; import org.briarproject.api.settings.Settings;
import org.briarproject.api.system.Clock;
import org.briarproject.api.system.LocationUtils; import org.briarproject.api.system.LocationUtils;
import org.briarproject.util.StringUtils; import org.briarproject.util.StringUtils;
@@ -79,7 +79,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final int SOCKS_PORT = 59050, CONTROL_PORT = 59051; private static final int SOCKS_PORT = 59050, CONTROL_PORT = 59051;
private static final int COOKIE_TIMEOUT = 3000; // Milliseconds private static final int COOKIE_TIMEOUT = 3000; // Milliseconds
private static final Pattern ONION = Pattern.compile("[a-z2-7]{16}"); private static final Pattern ONION = Pattern.compile("[a-z2-7]{16}");
private static final int MIN_DESCRIPTORS_PUBLISHED = 3;
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(TorPlugin.class.getName()); Logger.getLogger(TorPlugin.class.getName());
@@ -87,10 +86,10 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final Context appContext; private final Context appContext;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
private final DevReporter reporter; private final DevReporter reporter;
private final Clock clock; private final Backoff backoff;
private final DuplexPluginCallback callback; private final DuplexPluginCallback callback;
private final String architecture; private final String architecture;
private final int maxLatency, maxIdleTime, pollingInterval, socketTimeout; private final int maxLatency, maxIdleTime, socketTimeout;
private final ConnectionStatus connectionStatus; private final ConnectionStatus connectionStatus;
private final File torDirectory, torFile, geoIpFile, configFile; private final File torDirectory, torFile, geoIpFile, configFile;
private final File doneFile, cookieFile; private final File doneFile, cookieFile;
@@ -103,23 +102,22 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private volatile BroadcastReceiver networkStateReceiver = null; private volatile BroadcastReceiver networkStateReceiver = null;
TorPlugin(Executor ioExecutor, Context appContext, TorPlugin(Executor ioExecutor, Context appContext,
LocationUtils locationUtils, DevReporter reporter, Clock clock, LocationUtils locationUtils, DevReporter reporter, Backoff backoff,
DuplexPluginCallback callback, String architecture, int maxLatency, DuplexPluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, int pollingInterval) { int maxIdleTime) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.appContext = appContext; this.appContext = appContext;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
this.reporter = reporter; this.reporter = reporter;
this.clock = clock; this.backoff = backoff;
this.callback = callback; this.callback = callback;
this.architecture = architecture; this.architecture = architecture;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime; this.maxIdleTime = maxIdleTime;
this.pollingInterval = pollingInterval;
if (maxIdleTime > Integer.MAX_VALUE / 2) if (maxIdleTime > Integer.MAX_VALUE / 2)
socketTimeout = Integer.MAX_VALUE; socketTimeout = Integer.MAX_VALUE;
else socketTimeout = maxIdleTime * 2; else socketTimeout = maxIdleTime * 2;
connectionStatus = new ConnectionStatus(pollingInterval); connectionStatus = new ConnectionStatus();
torDirectory = appContext.getDir("tor", MODE_PRIVATE); torDirectory = appContext.getDir("tor", MODE_PRIVATE);
torFile = new File(torDirectory, "tor"); torFile = new File(torDirectory, "tor");
geoIpFile = new File(torDirectory, "geoip"); geoIpFile = new File(torDirectory, "geoip");
@@ -228,7 +226,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (phase != null && phase.contains("PROGRESS=100")) { if (phase != null && phase.contains("PROGRESS=100")) {
LOG.info("Tor has already bootstrapped"); LOG.info("Tor has already bootstrapped");
connectionStatus.setBootstrapped(); connectionStatus.setBootstrapped();
sendCrashReports(); sendDevReports();
} }
} }
// Register to receive network status events // Register to receive network status events
@@ -359,12 +357,12 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
} }
private void sendCrashReports() { private void sendDevReports() {
ioExecutor.execute(new Runnable() { ioExecutor.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
reporter.sendReports( File reportDir = AndroidUtils.getReportDir(appContext);
AndroidUtils.getReportDir(appContext), SOCKS_PORT); reporter.sendReports(reportDir, SOCKS_PORT);
} }
}); });
} }
@@ -404,6 +402,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
publishHiddenService(localPort); publishHiddenService(localPort);
} }
}); });
backoff.reset();
// Accept incoming hidden service connections from Tor // Accept incoming hidden service connections from Tor
acceptContactConnections(ss); acceptContactConnections(ss);
} }
@@ -470,6 +469,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return; return;
} }
LOG.info("Connection received"); LOG.info("Connection received");
backoff.reset();
TorTransportConnection conn = new TorTransportConnection(this, s); TorTransportConnection conn = new TorTransportConnection(this, s);
callback.incomingConnectionCreated(conn); callback.incomingConnectionCreated(conn);
} }
@@ -517,25 +517,25 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
public int getPollingInterval() { public int getPollingInterval() {
return pollingInterval; return backoff.getPollingInterval();
} }
public void poll(Collection<ContactId> connected) { public void poll(Collection<ContactId> connected) {
if (!isRunning()) return; if (!isRunning()) return;
if (connectionStatus.shouldPoll(clock.currentTimeMillis())) { backoff.increment();
// TODO: Pass properties to connectAndCallBack() // TODO: Pass properties to connectAndCallBack()
for (ContactId c : callback.getRemoteProperties().keySet()) for (ContactId c : callback.getRemoteProperties().keySet())
if (!connected.contains(c)) connectAndCallBack(c); if (!connected.contains(c)) connectAndCallBack(c);
} else {
LOG.info("Hidden service descriptor published, not polling");
}
} }
private void connectAndCallBack(final ContactId c) { private void connectAndCallBack(final ContactId c) {
ioExecutor.execute(new Runnable() { ioExecutor.execute(new Runnable() {
public void run() { public void run() {
DuplexTransportConnection d = createConnection(c); DuplexTransportConnection d = createConnection(c);
if (d != null) callback.outgoingConnectionCreated(c, d); if (d != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, d);
}
} }
}); });
} }
@@ -593,6 +593,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (status.equals("BUILT") && if (status.equals("BUILT") &&
connectionStatus.getAndSetCircuitBuilt()) { connectionStatus.getAndSetCircuitBuilt()) {
LOG.info("First circuit built"); LOG.info("First circuit built");
backoff.reset();
if (isRunning()) callback.transportEnabled(); if (isRunning()) callback.transportEnabled();
} }
} }
@@ -614,14 +615,15 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) {
connectionStatus.setBootstrapped(); connectionStatus.setBootstrapped();
sendCrashReports(); sendDevReports();
backoff.reset();
if (isRunning()) callback.transportEnabled(); if (isRunning()) callback.transportEnabled();
} }
} }
public void unrecognized(String type, String msg) { public void unrecognized(String type, String msg) {
if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) if (type.equals("HS_DESC") && msg.startsWith("UPLOADED"))
connectionStatus.descriptorPublished(clock.currentTimeMillis()); LOG.info("Descriptor uploaded");
} }
private static class WriteObserver extends FileObserver { private static class WriteObserver extends FileObserver {
@@ -707,17 +709,9 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static class ConnectionStatus { private static class ConnectionStatus {
private final int pollingInterval;
// All of the following are locking: this // All of the following are locking: this
private boolean networkEnabled = false; private boolean networkEnabled = false;
private boolean bootstrapped = false, circuitBuilt = false; private boolean bootstrapped = false, circuitBuilt = false;
private int descriptorsPublished = 0;
private long descriptorsPublishedTime = Long.MAX_VALUE;
private ConnectionStatus(int pollingInterval) {
this.pollingInterval = pollingInterval;
}
private synchronized void setBootstrapped() { private synchronized void setBootstrapped() {
bootstrapped = true; bootstrapped = true;
@@ -729,26 +723,13 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return firstCircuit; return firstCircuit;
} }
private synchronized void descriptorPublished(long now) {
descriptorsPublished++;
if (descriptorsPublished == MIN_DESCRIPTORS_PUBLISHED)
descriptorsPublishedTime = now;
}
private synchronized void enableNetwork(boolean enable) { private synchronized void enableNetwork(boolean enable) {
networkEnabled = enable; networkEnabled = enable;
circuitBuilt = false; circuitBuilt = false;
descriptorsPublished = 0;
descriptorsPublishedTime = Long.MAX_VALUE;
} }
private synchronized boolean isConnected() { private synchronized boolean isConnected() {
return networkEnabled && bootstrapped && circuitBuilt; return networkEnabled && bootstrapped && circuitBuilt;
} }
private synchronized boolean shouldPoll(long now) {
return descriptorsPublished < MIN_DESCRIPTORS_PUBLISHED
|| now - descriptorsPublishedTime < 2 * pollingInterval;
}
} }
} }

View File

@@ -6,13 +6,13 @@ import android.os.Build;
import org.briarproject.android.util.AndroidUtils; import org.briarproject.android.util.AndroidUtils;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventBus;
import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.BackoffFactory;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory; import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
import org.briarproject.api.reporting.DevReporter; import org.briarproject.api.reporting.DevReporter;
import org.briarproject.api.system.Clock;
import org.briarproject.api.system.LocationUtils; import org.briarproject.api.system.LocationUtils;
import org.briarproject.system.SystemClock;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -24,24 +24,26 @@ public class TorPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
private static final int POLLING_INTERVAL = 2 * 60 * 1000; // 2 minutes private static final int MIN_POLLING_INTERVAL = 2 * 60 * 1000; // 2 minutes
private static final int MAX_POLLING_INTERVAL = 60 * 60 * 1000; // 1 hour
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor; private final Executor ioExecutor;
private final Context appContext; private final Context appContext;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
private final DevReporter reporter; private final DevReporter reporter;
private final EventBus eventBus; private final EventBus eventBus;
private final Clock clock; private final BackoffFactory backoffFactory;
public TorPluginFactory(Executor ioExecutor, Context appContext, public TorPluginFactory(Executor ioExecutor, Context appContext,
LocationUtils locationUtils, DevReporter reporter, LocationUtils locationUtils, DevReporter reporter,
EventBus eventBus) { EventBus eventBus, BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.appContext = appContext; this.appContext = appContext;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
this.reporter = reporter; this.reporter = reporter;
this.eventBus = eventBus; this.eventBus = eventBus;
clock = new SystemClock(); this.backoffFactory = backoffFactory;
} }
public TransportId getId() { public TransportId getId() {
@@ -72,10 +74,11 @@ public class TorPluginFactory implements DuplexPluginFactory {
// Use position-independent executable for SDK >= 16 // Use position-independent executable for SDK >= 16
if (Build.VERSION.SDK_INT >= 16) architecture += "-pie"; if (Build.VERSION.SDK_INT >= 16) architecture += "-pie";
TorPlugin plugin = Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
new TorPlugin(ioExecutor, appContext, locationUtils, reporter, MAX_POLLING_INTERVAL, BACKOFF_BASE);
clock, callback, architecture, MAX_LATENCY, TorPlugin plugin = new TorPlugin(ioExecutor, appContext, locationUtils,
MAX_IDLE_TIME, POLLING_INTERVAL); reporter, backoff, callback, architecture, MAX_LATENCY,
MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }