Add bootstrap percentage and HS desc uploads to observer interface.

This commit is contained in:
akwizgran
2023-03-28 11:31:20 +01:00
parent 49f10e7e82
commit a468af94db
6 changed files with 113 additions and 56 deletions

View File

@@ -55,10 +55,10 @@ public class AndroidTorWrapper extends AbstractTorWrapper {
* @param ioExecutor The wrapper will use this executor to run IO tasks,
* some of which may run for the lifetime of the wrapper, so the executor
* should have an unlimited thread pool.
* @param eventExecutor The wrapper will use this executor to call
* {@link StateObserver#observeState(TorState)}. To ensure that state
* changes are observed in the order they occur, this executor should have
* a single thread (eg the app's main thread).
* @param eventExecutor The wrapper will use this executor to call the
* {@link Observer observer} (if any). To ensure that events are observed
* in the order they occur, this executor should have a single thread (eg
* the app's main thread).
* @param architecture The processor architecture of the Tor and pluggable
* transport binaries.
* @param torDirectory The directory where the Tor process should keep its

View File

@@ -28,6 +28,7 @@ import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType;
import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper;
import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.HiddenServiceProperties;
import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.Observer;
import org.briarproject.bramble.plugin.tor.wrapper.TorWrapper.TorState;
import org.briarproject.nullsafety.InterfaceNotNullByDefault;
import org.briarproject.nullsafety.NotNullByDefault;
@@ -148,10 +149,22 @@ class TorPlugin implements DuplexPlugin, EventListener {
// Don't execute more than one connection status check at a time
connectionStatusExecutor =
new PoliteExecutor("TorPlugin", ioExecutor, 1);
tor.setStateObserver(torState -> {
tor.setObserver(new Observer() {
@Override
public void onState(TorState torState) {
State s = state.getState(torState);
if (s == ACTIVE) backoff.reset();
callback.pluginStateChanged(s);
}
@Override
public void onBootstrapPercentage(int percentage) {
}
@Override
public void onHsDescriptorUpload(String onion) {
}
});
}

View File

@@ -21,6 +21,8 @@ import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
@@ -61,6 +63,8 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
private static final String OWNER = "__OwningControllerProcess";
private static final int COOKIE_TIMEOUT_MS = 3000;
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
private static final Pattern BOOTSTRAP_PERCENTAGE =
Pattern.compile("PROGRESS=(\\d{1,3})");
protected final Executor ioExecutor;
protected final Executor eventExecutor;
@@ -112,8 +116,8 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
}
@Override
public void setStateObserver(@Nullable StateObserver stateObserver) {
state.setObserver(stateObserver);
public void setObserver(@Nullable Observer observer) {
state.setObserver(observer);
}
@Override
@@ -172,9 +176,10 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
controlConnection.setEvents(asList(EVENTS));
// Check whether Tor has already bootstrapped
String info = controlConnection.getInfo("status/bootstrap-phase");
if (info != null && info.contains("PROGRESS=100")) {
LOG.info("Tor has already bootstrapped");
state.setBootstrapped();
if (info != null && info.contains("PROGRESS=")) {
int percentage = parseBootstrapPercentage(info);
if (percentage == 100) LOG.info("Tor has already bootstrapped");
state.setBootstrapPercentage(percentage);
}
// Check whether Tor has already built a circuit
info = controlConnection.getInfo("status/circuit-established");
@@ -410,7 +415,9 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
if (parts.length < 2) {
LOG.warning("Failed to parse HS_DESC UPLOADED event");
} else if (LOG.isLoggable(INFO)) {
LOG.info("V3 descriptor uploaded for " + scrubOnion(parts[1]));
String onion = parts[1];
LOG.info("V3 descriptor uploaded for " + scrubOnion(onion));
state.onHsDescriptorUploaded(onion);
}
}
}
@@ -420,9 +427,10 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
}
private void handleClientStatus(String msg) {
if (msg.startsWith("BOOTSTRAP PROGRESS=100")) {
LOG.info("Bootstrapped");
state.setBootstrapped();
if (msg.startsWith("BOOTSTRAP PROGRESS=")) {
int percentage = parseBootstrapPercentage(msg);
if (percentage == 100) LOG.info("Bootstrapped");
state.setBootstrapPercentage(percentage);
} else if (msg.startsWith("CIRCUIT_ESTABLISHED")) {
if (state.setCircuitBuilt(true)) LOG.info("Circuit built");
} else if (msg.startsWith("CIRCUIT_NOT_ESTABLISHED")) {
@@ -435,6 +443,21 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
}
}
private int parseBootstrapPercentage(String s) {
Matcher matcher = BOOTSTRAP_PERCENTAGE.matcher(s);
if (matcher.matches()) {
try {
return Integer.parseInt(matcher.group(1));
} catch (NumberFormatException e) {
// Fall through
}
}
if (LOG.isLoggable(WARNING)) {
LOG.warning("Failed to parse bootstrap percentage: " + s);
}
return 0;
}
private void handleGeneralStatus(String msg) {
if (msg.startsWith("CLOCK_JUMPED")) {
Long time = parseLongArgument(msg, "TIME");
@@ -512,7 +535,7 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
@GuardedBy("this")
@Nullable
private StateObserver observer = null;
private Observer observer = null;
@GuardedBy("this")
private boolean started = false,
@@ -521,9 +544,14 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
networkEnabled = false,
paddingEnabled = false,
ipv6Enabled = false,
bootstrapped = false,
circuitBuilt = false;
@GuardedBy("this")
private int bootstrapPercentage = 0;
@GuardedBy("this")
private List<String> bridges = emptyList();
@GuardedBy("this")
private int orConnectionsConnected = 0;
@@ -532,32 +560,25 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
private TorState state = null;
private synchronized void setObserver(
@Nullable StateObserver observer) {
@Nullable Observer observer) {
this.observer = observer;
}
@GuardedBy("this")
private void updateObserver() {
private void updateState() {
TorState newState = getState();
if (newState != state) {
state = newState;
// Post the new state to the event executor. The contract of
// this executor is to execute tasks in the order they're
// submitted, so state changes will be observed in the correct
// order but outside the lock
if (observer != null) {
eventExecutor.execute(() ->
observer.observeState(newState));
// Notify the observer on the event executor
eventExecutor.execute(() -> observer.onState(newState));
}
}
}
@GuardedBy("this")
private List<String> bridges = emptyList();
private synchronized void setStarted() {
started = true;
updateObserver();
updateState();
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
@@ -567,13 +588,18 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
private synchronized void setStopped() {
stopped = true;
updateObserver();
updateState();
}
private synchronized void setBootstrapped() {
if (bootstrapped) return;
bootstrapped = true;
updateObserver();
private synchronized void setBootstrapPercentage(int percentage) {
if (percentage == bootstrapPercentage) return;
bootstrapPercentage = percentage;
if (observer != null) {
// Notify the observer on the event executor
eventExecutor.execute(() ->
observer.onBootstrapPercentage(percentage));
}
updateState();
}
/**
@@ -583,7 +609,7 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
private synchronized boolean setCircuitBuilt(boolean built) {
if (built == circuitBuilt) return false; // Unchanged
circuitBuilt = built;
updateObserver();
updateState();
return true; // Changed
}
@@ -598,7 +624,7 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
networkEnabled = enable;
if (!enable) circuitBuilt = false;
if (!wasInitialised || enable != wasEnabled) {
updateObserver();
updateState();
}
return enable != wasEnabled;
}
@@ -639,15 +665,15 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
if (!started || stopped) return STARTING_STOPPING;
if (!networkInitialised) return CONNECTING;
if (!networkEnabled) return DISABLED;
return bootstrapped && circuitBuilt && orConnectionsConnected > 0
? CONNECTED : CONNECTING;
return bootstrapPercentage == 100 && circuitBuilt
&& orConnectionsConnected > 0 ? CONNECTED : CONNECTING;
}
private synchronized void onOrConnectionConnected() {
int oldConnected = orConnectionsConnected;
orConnectionsConnected++;
logOrConnections();
if (oldConnected == 0) updateObserver();
if (oldConnected == 0) updateState();
}
private synchronized void onOrConnectionClosed() {
@@ -659,7 +685,7 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
}
logOrConnections();
if (orConnectionsConnected == 0 && oldConnected != 0) {
updateObserver();
updateState();
}
}
@@ -669,5 +695,13 @@ abstract class AbstractTorWrapper implements EventHandler, TorWrapper {
LOG.info(orConnectionsConnected + " OR connections connected");
}
}
private synchronized void onHsDescriptorUploaded(String onion) {
if (observer != null) {
// Notify the observer on the event executor
eventExecutor.execute(() ->
observer.onHsDescriptorUpload(onion));
}
}
}
}

View File

@@ -34,7 +34,7 @@ public interface TorWrapper {
* existing observer, or removes any existing observer if the argument is
* null.
*/
void setStateObserver(@Nullable StateObserver stateObserver);
void setObserver(@Nullable Observer observer);
/**
* Returns the current state of the wrapper.
@@ -129,16 +129,25 @@ public interface TorWrapper {
/**
* An interface for observing changes to the {@link TorState state} of the
* Tor process.
* Tor process. All calls happen on the event executor supplied to the
* wrapper's constructor.
*/
interface StateObserver {
interface Observer {
/**
* This method is called whenever the state of the Tor process changes.
* The call happens on the event executor supplied to the wrapper's
* constructor.
* Called whenever the state of the Tor process changes.
*/
void observeState(TorState s);
void onState(TorState s);
/**
* Called whenever the bootstrap percentage changes.
*/
void onBootstrapPercentage(int percentage);
/**
* Called whenever a hidden service descriptor is uploaded.
*/
void onHsDescriptorUpload(String onion);
}
class HiddenServiceProperties {

View File

@@ -18,10 +18,10 @@ public class UnixTorWrapper extends JavaTorWrapper {
* @param ioExecutor The wrapper will use this executor to run IO tasks,
* some of which may run for the lifetime of the wrapper, so the executor
* should have an unlimited thread pool.
* @param eventExecutor The wrapper will use this executor to call
* {@link StateObserver#observeState(TorState)}. To ensure that state
* changes are observed in the order they occur, this executor should have
* a single thread (eg the app's main thread).
* @param eventExecutor The wrapper will use this executor to call the
* {@link Observer observer} (if any). To ensure that events are observed
* in the order they occur, this executor should have a single thread (eg
* the app's main thread).
* @param architecture The processor architecture of the Tor and pluggable
* transport binaries.
* @param torDirectory The directory where the Tor process should keep its

View File

@@ -24,9 +24,10 @@ public class WindowsTorWrapper extends JavaTorWrapper {
* some of which may run for the lifetime of the wrapper, so the executor
* should have an unlimited thread pool.
* @param eventExecutor The wrapper will use this executor to call
* {@link StateObserver#observeState(TorState)}. To ensure that state
* changes are observed in the order they occur, this executor should have
* a single thread (eg the app's main thread).
* @param eventExecutor The wrapper will use this executor to call the
* {@link Observer observer} (if any). To ensure that events are observed
* in the order they occur, this executor should have a single thread (eg
* the app's main thread).
* @param architecture The processor architecture of the Tor and pluggable
* transport binaries.
* @param torDirectory The directory where the Tor process should keep its