diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java index e39fec2d3..014d5d8b3 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java @@ -43,11 +43,12 @@ class AndroidTorPlugin extends TorPlugin { BatteryManager batteryManager, TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback, String architecture, int maxLatency, - int maxIdleTime, int pollingInterval) { + int maxIdleTime, int initialPollingInterval, + int stablePollingInterval) { super(ioExecutor, networkManager, locationUtils, torSocketFactory, clock, resourceProvider, circumventionProvider, batteryManager, torRendezvousCrypto, callback, architecture, maxLatency, - maxIdleTime, pollingInterval, + maxIdleTime, initialPollingInterval, stablePollingInterval, appContext.getDir("tor", MODE_PRIVATE)); this.appContext = appContext; PowerManager pm = (PowerManager) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPluginFactory.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPluginFactory.java index 7737956d2..7a0888d72 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPluginFactory.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPluginFactory.java @@ -25,17 +25,31 @@ import javax.net.SocketFactory; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; +import static java.util.logging.Logger.getLogger; @Immutable @NotNullByDefault public class AndroidTorPluginFactory implements DuplexPluginFactory { private static final Logger LOG = - Logger.getLogger(AndroidTorPluginFactory.class.getName()); + getLogger(AndroidTorPluginFactory.class.getName()); private static final int MAX_LATENCY = (int) SECONDS.toMillis(30); private static final int MAX_IDLE_TIME = (int) SECONDS.toMillis(30); - private static final int POLLING_INTERVAL = (int) MINUTES.toMillis(1); + + /** + * How often to poll before our hidden service becomes reachable. + */ + private static final int INITIAL_POLLING_INTERVAL = + (int) MINUTES.toMillis(1); + + /** + * How often to poll when our hidden service is reachable. Our contacts + * will poll when they come online, so our polling is just a fallback in + * case of repeated connection failures. + */ + private static final int STABLE_POLLING_INTERVAL = + (int) MINUTES.toMillis(15); private final Executor ioExecutor; private final ScheduledExecutorService scheduler; @@ -111,7 +125,8 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory { appContext, networkManager, locationUtils, torSocketFactory, clock, resourceProvider, circumventionProvider, batteryManager, torRendezvousCrypto, callback, architecture, MAX_LATENCY, - MAX_IDLE_TIME, POLLING_INTERVAL); + MAX_IDLE_TIME, INITIAL_POLLING_INTERVAL, + STABLE_POLLING_INTERVAL); eventBus.addListener(plugin); return plugin; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index a80e90eaa..81d8b6773 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -110,15 +110,46 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private static final Logger LOG = getLogger(TorPlugin.class.getName()); + /** + * Controller events we want to receive. + */ private static final String[] EVENTS = { "CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR" }; + + /** + * Command-line argument to set our process as Tor's owning controller + * so Tor exits when our process dies. + */ private static final String OWNER = "__OwningControllerProcess"; + + /** + * How long to wait for the authentication cookie file to be created. + */ private static final int COOKIE_TIMEOUT_MS = 3000; + + /** + * How often to check whether the authentication cookie file has been + * created. + */ private static final int COOKIE_POLLING_INTERVAL_MS = 200; + + /** + * Regex for matching v2 hidden service names. + */ private static final Pattern ONION_V2 = Pattern.compile("[a-z2-7]{16}"); + + /** + * Regex for matching v3 hidden service names. + */ private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}"); + /** + * How many copies of our descriptor must be uploaded before we consider + * our hidden service to be reachable and switch to less frequent polling. + */ + private static final int MIN_DESCRIPTORS_UPLOADED = 5; + private final Executor ioExecutor, connectionStatusExecutor; private final NetworkManager networkManager; private final LocationUtils locationUtils; @@ -130,7 +161,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { private final String architecture; private final CircumventionProvider circumventionProvider; private final ResourceProvider resourceProvider; - private final int maxLatency, maxIdleTime, pollingInterval, socketTimeout; + private final int maxLatency, maxIdleTime, socketTimeout; + private final int initialPollingInterval, stablePollingInterval; private final File torDirectory, torFile, geoIpFile, obfs4File, configFile; private final File doneFile, cookieFile; private final AtomicBoolean used = new AtomicBoolean(false); @@ -152,7 +184,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { BatteryManager batteryManager, TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback, String architecture, int maxLatency, - int maxIdleTime, int pollingInterval, File torDirectory) { + int maxIdleTime, int initialPollingInterval, + int stablePollingInterval, File torDirectory) { this.ioExecutor = ioExecutor; this.networkManager = networkManager; this.locationUtils = locationUtils; @@ -166,10 +199,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { this.architecture = architecture; this.maxLatency = maxLatency; this.maxIdleTime = maxIdleTime; - this.pollingInterval = pollingInterval; if (maxIdleTime > Integer.MAX_VALUE / 2) socketTimeout = Integer.MAX_VALUE; else socketTimeout = maxIdleTime * 2; + this.initialPollingInterval = initialPollingInterval; + this.stablePollingInterval = stablePollingInterval; this.torDirectory = torDirectory; torFile = new File(torDirectory, "tor"); geoIpFile = new File(torDirectory, "geoip"); @@ -604,7 +638,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public int getPollingInterval() { - return pollingInterval; + if (state.isDescriptorPublished()) { + LOG.info("Using stable polling interval"); + return stablePollingInterval; + } else { + LOG.info("Using initial polling interval"); + return initialPollingInterval; + } } @Override @@ -788,13 +828,12 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @Override public void unrecognized(String type, String msg) { if (type.equals("HS_DESC") && msg.startsWith("UPLOADED")) { - if (LOG.isLoggable(INFO)) { - String[] words = msg.split(" "); - if (words.length > 1 && ONION_V3.matcher(words[1]).matches()) { - LOG.info("V3 descriptor uploaded"); - } else { - LOG.info("V2 descriptor uploaded"); - } + String[] words = msg.split(" "); + if (words.length > 1 && ONION_V3.matcher(words[1]).matches()) { + LOG.info("V3 descriptor uploaded"); + state.descriptorUploaded(); + } else { + LOG.info("V2 descriptor uploaded"); } } } @@ -953,6 +992,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { @GuardedBy("this") private final Set orConnections = new HashSet<>(); + @GuardedBy("this") + private int descriptorsUploaded = 0; + synchronized void setStarted() { started = true; callback.pluginStateChanged(getState()); @@ -986,7 +1028,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { synchronized void enableNetwork(boolean enable) { networkInitialised = true; networkEnabled = enable; - if (!enable) circuitBuilt = false; + if (!enable) { + circuitBuilt = false; + descriptorsUploaded = 0; + } callback.pluginStateChanged(getState()); } @@ -1003,6 +1048,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } else { orConnections.remove(orName); + if (orConnections.isEmpty()) descriptorsUploaded = 0; } if (LOG.isLoggable(INFO)) { LOG.info(orConnections.size() + " OR connections"); @@ -1010,6 +1056,19 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { callback.pluginStateChanged(getState()); } + // Doesn't affect getState() + synchronized void descriptorUploaded() { + if (networkEnabled && !orConnections.isEmpty()) { + descriptorsUploaded++; + } else { + LOG.warning("Descriptor was uploaded with no OR connection"); + } + } + + synchronized boolean isDescriptorPublished() { + return descriptorsUploaded >= MIN_DESCRIPTORS_UPLOADED; + } + // Doesn't affect getState() synchronized boolean setServerSocket(ServerSocket ss) { if (stopped || serverSocket != null) return false; diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/JavaTorPlugin.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/JavaTorPlugin.java index e5e1e56dc..98f929372 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/JavaTorPlugin.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/JavaTorPlugin.java @@ -26,11 +26,13 @@ abstract class JavaTorPlugin extends TorPlugin { BatteryManager batteryManager, TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback, String architecture, int maxLatency, - int maxIdleTime, int pollingInterval, File torDirectory) { + int maxIdleTime, int initialPollingInterval, + int stablePollingInterval, File torDirectory) { super(ioExecutor, networkManager, locationUtils, torSocketFactory, clock, resourceProvider, circumventionProvider, batteryManager, torRendezvousCrypto, callback, architecture, maxLatency, - maxIdleTime, pollingInterval, torDirectory); + maxIdleTime, initialPollingInterval, stablePollingInterval, + torDirectory); } @Override diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPlugin.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPlugin.java index 6edb88674..39c6243ed 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPlugin.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPlugin.java @@ -26,11 +26,13 @@ class UnixTorPlugin extends JavaTorPlugin { BatteryManager batteryManager, TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback, String architecture, int maxLatency, - int maxIdleTime, int pollingInterval, File torDirectory) { + int maxIdleTime, int initialPollingInterval, + int stablePollingInterval, File torDirectory) { super(ioExecutor, networkManager, locationUtils, torSocketFactory, clock, resourceProvider, circumventionProvider, batteryManager, - torRendezvousCrypto, callback, architecture, maxLatency, - maxIdleTime, pollingInterval, torDirectory); + torRendezvousCrypto, callback, architecture, + maxLatency, maxIdleTime, initialPollingInterval, + stablePollingInterval, torDirectory); } @Override diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPluginFactory.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPluginFactory.java index 23323db1f..182fecebb 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPluginFactory.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/tor/UnixTorPluginFactory.java @@ -34,7 +34,20 @@ public class UnixTorPluginFactory implements DuplexPluginFactory { private static final int MAX_LATENCY = (int) SECONDS.toMillis(30); private static final int MAX_IDLE_TIME = (int) SECONDS.toMillis(30); - private static final int POLLING_INTERVAL = (int) MINUTES.toMillis(1); + + /** + * How often to poll before our hidden service becomes reachable. + */ + private static final int INITIAL_POLLING_INTERVAL = + (int) MINUTES.toMillis(1); + + /** + * How often to poll when our hidden service is reachable. Our contacts + * will poll when they come online, so our polling is just a fallback in + * case of repeated connection failures. + */ + private static final int STABLE_POLLING_INTERVAL = + (int) MINUTES.toMillis(15); private final Executor ioExecutor; private final NetworkManager networkManager; @@ -95,7 +108,8 @@ public class UnixTorPluginFactory implements DuplexPluginFactory { locationUtils, torSocketFactory, clock, resourceProvider, circumventionProvider, batteryManager, torRendezvousCrypto, callback, architecture, MAX_LATENCY, MAX_IDLE_TIME, - POLLING_INTERVAL, torDirectory); + INITIAL_POLLING_INTERVAL, STABLE_POLLING_INTERVAL, + torDirectory); eventBus.addListener(plugin); return plugin; }