Poll Tor plugin infrequently once our HS is reachable.

This commit is contained in:
akwizgran
2020-01-23 17:43:18 +00:00
parent 94f8f68336
commit 98e5d892a4
6 changed files with 117 additions and 24 deletions

View File

@@ -43,11 +43,12 @@ class AndroidTorPlugin extends TorPlugin {
BatteryManager batteryManager, BatteryManager batteryManager,
TorRendezvousCrypto torRendezvousCrypto, TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency, PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, int pollingInterval) { int maxIdleTime, int initialPollingInterval,
int stablePollingInterval) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory, super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager, clock, resourceProvider, circumventionProvider, batteryManager,
torRendezvousCrypto, callback, architecture, maxLatency, torRendezvousCrypto, callback, architecture, maxLatency,
maxIdleTime, pollingInterval, maxIdleTime, initialPollingInterval, stablePollingInterval,
appContext.getDir("tor", MODE_PRIVATE)); appContext.getDir("tor", MODE_PRIVATE));
this.appContext = appContext; this.appContext = appContext;
PowerManager pm = (PowerManager) PowerManager pm = (PowerManager)

View File

@@ -25,17 +25,31 @@ import javax.net.SocketFactory;
import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Logger.getLogger;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class AndroidTorPluginFactory implements DuplexPluginFactory { public class AndroidTorPluginFactory implements DuplexPluginFactory {
private static final Logger LOG = 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_LATENCY = (int) SECONDS.toMillis(30);
private static final int MAX_IDLE_TIME = (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 Executor ioExecutor;
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
@@ -111,7 +125,8 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
appContext, networkManager, locationUtils, torSocketFactory, appContext, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager, clock, resourceProvider, circumventionProvider, batteryManager,
torRendezvousCrypto, callback, architecture, MAX_LATENCY, torRendezvousCrypto, callback, architecture, MAX_LATENCY,
MAX_IDLE_TIME, POLLING_INTERVAL); MAX_IDLE_TIME, INITIAL_POLLING_INTERVAL,
STABLE_POLLING_INTERVAL);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -110,15 +110,46 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final Logger LOG = getLogger(TorPlugin.class.getName()); private static final Logger LOG = getLogger(TorPlugin.class.getName());
/**
* Controller events we want to receive.
*/
private static final String[] EVENTS = { private static final String[] EVENTS = {
"CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR" "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"; 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; 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; 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}"); 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}"); 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 Executor ioExecutor, connectionStatusExecutor;
private final NetworkManager networkManager; private final NetworkManager networkManager;
private final LocationUtils locationUtils; private final LocationUtils locationUtils;
@@ -130,7 +161,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final String architecture; private final String architecture;
private final CircumventionProvider circumventionProvider; private final CircumventionProvider circumventionProvider;
private final ResourceProvider resourceProvider; 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 torDirectory, torFile, geoIpFile, obfs4File, configFile;
private final File doneFile, cookieFile; private final File doneFile, cookieFile;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@@ -152,7 +184,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
BatteryManager batteryManager, BatteryManager batteryManager,
TorRendezvousCrypto torRendezvousCrypto, TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency, PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, int pollingInterval, File torDirectory) { int maxIdleTime, int initialPollingInterval,
int stablePollingInterval, File torDirectory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.networkManager = networkManager; this.networkManager = networkManager;
this.locationUtils = locationUtils; this.locationUtils = locationUtils;
@@ -166,10 +199,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
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;
this.initialPollingInterval = initialPollingInterval;
this.stablePollingInterval = stablePollingInterval;
this.torDirectory = torDirectory; this.torDirectory = torDirectory;
torFile = new File(torDirectory, "tor"); torFile = new File(torDirectory, "tor");
geoIpFile = new File(torDirectory, "geoip"); geoIpFile = new File(torDirectory, "geoip");
@@ -604,7 +638,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public int getPollingInterval() { 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 @Override
@@ -788,13 +828,12 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
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")) {
if (LOG.isLoggable(INFO)) { String[] words = msg.split(" ");
String[] words = msg.split(" "); if (words.length > 1 && ONION_V3.matcher(words[1]).matches()) {
if (words.length > 1 && ONION_V3.matcher(words[1]).matches()) { LOG.info("V3 descriptor uploaded");
LOG.info("V3 descriptor uploaded"); state.descriptorUploaded();
} else { } else {
LOG.info("V2 descriptor uploaded"); LOG.info("V2 descriptor uploaded");
}
} }
} }
} }
@@ -953,6 +992,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@GuardedBy("this") @GuardedBy("this")
private final Set<String> orConnections = new HashSet<>(); private final Set<String> orConnections = new HashSet<>();
@GuardedBy("this")
private int descriptorsUploaded = 0;
synchronized void setStarted() { synchronized void setStarted() {
started = true; started = true;
callback.pluginStateChanged(getState()); callback.pluginStateChanged(getState());
@@ -986,7 +1028,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
synchronized void enableNetwork(boolean enable) { synchronized void enableNetwork(boolean enable) {
networkInitialised = true; networkInitialised = true;
networkEnabled = enable; networkEnabled = enable;
if (!enable) circuitBuilt = false; if (!enable) {
circuitBuilt = false;
descriptorsUploaded = 0;
}
callback.pluginStateChanged(getState()); callback.pluginStateChanged(getState());
} }
@@ -1003,6 +1048,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
} else { } else {
orConnections.remove(orName); orConnections.remove(orName);
if (orConnections.isEmpty()) descriptorsUploaded = 0;
} }
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info(orConnections.size() + " OR connections"); LOG.info(orConnections.size() + " OR connections");
@@ -1010,6 +1056,19 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
callback.pluginStateChanged(getState()); 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() // Doesn't affect getState()
synchronized boolean setServerSocket(ServerSocket ss) { synchronized boolean setServerSocket(ServerSocket ss) {
if (stopped || serverSocket != null) return false; if (stopped || serverSocket != null) return false;

View File

@@ -26,11 +26,13 @@ abstract class JavaTorPlugin extends TorPlugin {
BatteryManager batteryManager, BatteryManager batteryManager,
TorRendezvousCrypto torRendezvousCrypto, TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency, 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, super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager, clock, resourceProvider, circumventionProvider, batteryManager,
torRendezvousCrypto, callback, architecture, maxLatency, torRendezvousCrypto, callback, architecture, maxLatency,
maxIdleTime, pollingInterval, torDirectory); maxIdleTime, initialPollingInterval, stablePollingInterval,
torDirectory);
} }
@Override @Override

View File

@@ -26,11 +26,13 @@ class UnixTorPlugin extends JavaTorPlugin {
BatteryManager batteryManager, BatteryManager batteryManager,
TorRendezvousCrypto torRendezvousCrypto, TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency, 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, super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager, clock, resourceProvider, circumventionProvider, batteryManager,
torRendezvousCrypto, callback, architecture, maxLatency, torRendezvousCrypto, callback, architecture,
maxIdleTime, pollingInterval, torDirectory); maxLatency, maxIdleTime, initialPollingInterval,
stablePollingInterval, torDirectory);
} }
@Override @Override

View File

@@ -34,7 +34,20 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = (int) SECONDS.toMillis(30); 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 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 Executor ioExecutor;
private final NetworkManager networkManager; private final NetworkManager networkManager;
@@ -95,7 +108,8 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
locationUtils, torSocketFactory, clock, resourceProvider, locationUtils, torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, torRendezvousCrypto, circumventionProvider, batteryManager, torRendezvousCrypto,
callback, architecture, MAX_LATENCY, MAX_IDLE_TIME, callback, architecture, MAX_LATENCY, MAX_IDLE_TIME,
POLLING_INTERVAL, torDirectory); INITIAL_POLLING_INTERVAL, STABLE_POLLING_INTERVAL,
torDirectory);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }