Merge branch '250-tor-polling' into 'master'

New polling logic for Tor. #250

Poll for outgoing connections each time the network is enabled, then disable polling once our hidden service descriptor has been published. If our contacts do the same, any contacts that are offline when we poll will connect to us when they come online.

This should reduce bandwidth and battery usage. The idea comes from Ricochet:

https://github.com/ricochet-im/ricochet/issues/68

Fixes #250.

See merge request !103
This commit is contained in:
str4d
2016-02-25 14:54:49 +00:00
3 changed files with 38 additions and 7 deletions

View File

@@ -24,6 +24,7 @@ import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import org.briarproject.api.properties.TransportProperties;
import org.briarproject.api.settings.Settings;
import org.briarproject.api.system.Clock;
import org.briarproject.api.system.LocationUtils;
import org.briarproject.util.StringUtils;
@@ -44,6 +45,7 @@ import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.zip.ZipInputStream;
@@ -63,7 +65,7 @@ class TorPlugin implements DuplexPlugin, EventHandler,
static final TransportId ID = new TransportId("tor");
private static final String[] EVENTS = {
"CIRC", "ORCONN", "NOTICE", "WARN", "ERR"
"CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR"
};
private static final String OWNER = "__OwningControllerProcess";
private static final int SOCKS_PORT = 59050, CONTROL_PORT = 59051;
@@ -71,23 +73,27 @@ class TorPlugin implements DuplexPlugin, EventHandler,
private static final int HOSTNAME_TIMEOUT = 30 * 1000; // Milliseconds
private static final Pattern ONION =
Pattern.compile("[a-z2-7]{16}\\.onion");
private static final int MIN_DESCRIPTORS_PUBLISHED = 3;
private static final Logger LOG =
Logger.getLogger(TorPlugin.class.getName());
private final Executor ioExecutor;
private final Context appContext;
private final LocationUtils locationUtils;
private final Clock clock;
private final DuplexPluginCallback callback;
private final String architecture;
private final int maxLatency, maxIdleTime, pollingInterval, socketTimeout;
private final File torDirectory, torFile, geoIpFile, configFile, doneFile;
private final File cookieFile, hostnameFile;
private final AtomicBoolean circuitBuilt;
private final AtomicInteger descriptorsPublished;
private volatile boolean running = false, networkEnabled = false;
private volatile boolean bootstrapped = false;
private volatile boolean connectedToWifi = false;
private volatile boolean online = false;
private volatile long descriptorsPublishedTime = Long.MAX_VALUE;
private volatile ServerSocket socket = null;
private volatile Socket controlSocket = null;
@@ -95,12 +101,13 @@ class TorPlugin implements DuplexPlugin, EventHandler,
private volatile BroadcastReceiver networkStateReceiver = null;
TorPlugin(Executor ioExecutor, Context appContext,
LocationUtils locationUtils, DuplexPluginCallback callback,
String architecture, int maxLatency, int maxIdleTime,
int pollingInterval) {
LocationUtils locationUtils, Clock clock,
DuplexPluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, int pollingInterval) {
this.ioExecutor = ioExecutor;
this.appContext = appContext;
this.locationUtils = locationUtils;
this.clock = clock;
this.callback = callback;
this.architecture = architecture;
this.maxLatency = maxLatency;
@@ -117,6 +124,7 @@ class TorPlugin implements DuplexPlugin, EventHandler,
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
hostnameFile = new File(torDirectory, "hs/hostname");
circuitBuilt = new AtomicBoolean(false);
descriptorsPublished = new AtomicInteger(0);
}
public TransportId getId() {
@@ -462,6 +470,8 @@ class TorPlugin implements DuplexPlugin, EventHandler,
if (LOG.isLoggable(INFO)) LOG.info("Enabling network: " + enable);
if (!enable) {
circuitBuilt.set(false);
descriptorsPublished.set(0);
descriptorsPublishedTime = Long.MAX_VALUE;
callback.transportDisabled();
}
networkEnabled = enable;
@@ -503,6 +513,14 @@ class TorPlugin implements DuplexPlugin, EventHandler,
public void poll(Collection<ContactId> connected) {
if (!isRunning()) return;
if (descriptorsPublished.get() >= MIN_DESCRIPTORS_PUBLISHED) {
long now = clock.currentTimeMillis();
if (now - descriptorsPublishedTime >= 2 * pollingInterval) {
LOG.info("Hidden service descriptor published, not polling");
return;
}
}
// TODO: Pass properties to connectAndCallBack()
for (ContactId c : callback.getRemoteProperties().keySet())
if (!connected.contains(c)) connectAndCallBack(c);
}
@@ -576,7 +594,15 @@ class TorPlugin implements DuplexPlugin, EventHandler,
}
}
public void unrecognized(String type, String msg) {}
public void unrecognized(String type, String msg) {
if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) {
int descriptors = descriptorsPublished.incrementAndGet();
if (descriptors == MIN_DESCRIPTORS_PUBLISHED) {
LOG.info("Hidden service descriptor published");
descriptorsPublishedTime = clock.currentTimeMillis();
}
}
}
private static class WriteObserver extends FileObserver {

View File

@@ -9,7 +9,9 @@ import org.briarproject.api.event.EventBus;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
import org.briarproject.api.system.Clock;
import org.briarproject.api.system.LocationUtils;
import org.briarproject.system.SystemClock;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -21,12 +23,13 @@ public class TorPluginFactory implements DuplexPluginFactory {
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 POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes
private static final int POLLING_INTERVAL = 2 * 60 * 1000; // 2 minutes
private final Executor ioExecutor;
private final Context appContext;
private final LocationUtils locationUtils;
private final EventBus eventBus;
private final Clock clock;
public TorPluginFactory(Executor ioExecutor, Context appContext,
LocationUtils locationUtils, EventBus eventBus) {
@@ -34,6 +37,7 @@ public class TorPluginFactory implements DuplexPluginFactory {
this.appContext = appContext;
this.locationUtils = locationUtils;
this.eventBus = eventBus;
clock = new SystemClock();
}
public TransportId getId() {
@@ -61,7 +65,7 @@ public class TorPluginFactory implements DuplexPluginFactory {
if (Build.VERSION.SDK_INT >= 16) architecture += "-pie";
TorPlugin plugin = new TorPlugin(ioExecutor, appContext, locationUtils,
callback, architecture, MAX_LATENCY, MAX_IDLE_TIME,
clock, callback, architecture, MAX_LATENCY, MAX_IDLE_TIME,
POLLING_INTERVAL);
eventBus.addListener(plugin);
return plugin;

View File

@@ -176,6 +176,7 @@ abstract class TcpPlugin implements DuplexPlugin {
public void poll(Collection<ContactId> connected) {
if (!isRunning()) return;
backoff.increment();
// TODO: Pass properties to connectAndCallBack()
for (ContactId c : callback.getRemoteProperties().keySet())
if (!connected.contains(c)) connectAndCallBack(c);
}