Skip to content
Snippets Groups Projects
Verified Commit 690673e9 authored by Torsten Grote's avatar Torsten Grote
Browse files

Wait until Tor becomes active

The TorPlugin publishes a StateFlow with its own state that consumers can collect and wait for the right state.

This is implemented for the CLI for now.
parent 56406d27
No related branches found
No related tags found
1 merge request!52Use existence of DB folder to signal if the mailbox is set up or not
Pipeline #9448 passed
...@@ -25,6 +25,8 @@ import com.github.ajalt.clikt.core.CliktCommand ...@@ -25,6 +25,8 @@ import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.counted import com.github.ajalt.clikt.parameters.options.counted
import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.options.option
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.runBlocking
import org.briarproject.mailbox.core.CoreEagerSingletons import org.briarproject.mailbox.core.CoreEagerSingletons
import org.briarproject.mailbox.core.JavaCliEagerSingletons import org.briarproject.mailbox.core.JavaCliEagerSingletons
import org.briarproject.mailbox.core.db.TransactionManager import org.briarproject.mailbox.core.db.TransactionManager
...@@ -33,6 +35,7 @@ import org.briarproject.mailbox.core.setup.QrCodeEncoder ...@@ -33,6 +35,7 @@ import org.briarproject.mailbox.core.setup.QrCodeEncoder
import org.briarproject.mailbox.core.setup.SetupManager import org.briarproject.mailbox.core.setup.SetupManager
import org.briarproject.mailbox.core.setup.WipeManager import org.briarproject.mailbox.core.setup.WipeManager
import org.briarproject.mailbox.core.system.InvalidIdException import org.briarproject.mailbox.core.system.InvalidIdException
import org.briarproject.mailbox.core.tor.TorPlugin
import org.slf4j.LoggerFactory.getLogger import org.slf4j.LoggerFactory.getLogger
import java.util.logging.Level.ALL import java.util.logging.Level.ALL
import java.util.logging.Level.INFO import java.util.logging.Level.INFO
...@@ -77,6 +80,9 @@ class Main : CliktCommand( ...@@ -77,6 +80,9 @@ class Main : CliktCommand(
@Inject @Inject
internal lateinit var wipeManager: WipeManager internal lateinit var wipeManager: WipeManager
@Inject
internal lateinit var torPlugin: TorPlugin
@Inject @Inject
internal lateinit var qrCodeEncoder: QrCodeEncoder internal lateinit var qrCodeEncoder: QrCodeEncoder
...@@ -138,11 +144,20 @@ class Main : CliktCommand( ...@@ -138,11 +144,20 @@ class Main : CliktCommand(
setupManager.getOwnerToken(txn) != null setupManager.getOwnerToken(txn) != null
} }
if (!ownerTokenExists) { if (!ownerTokenExists) {
// TODO remove before release if (debug) {
val token = setupToken ?: db.read { setupManager.getSetupToken(it) } val token = setupToken ?: db.read { setupManager.getSetupToken(it) }
println("curl -v -H \"Authorization: Bearer $token\" -X PUT http://localhost:8000/setup") println(
// FIXME: We need to wait for the hidden service address to become available "curl -v -H \"Authorization: Bearer $token\" -X PUT " +
"http://localhost:8000/setup"
)
}
// If not set up, show QR code for manual setup // If not set up, show QR code for manual setup
runBlocking {
// wait until Tor becomes active and published the onion service
torPlugin.state.takeWhile { state ->
state != TorPlugin.State.ACTIVE
}
}
qrCodeEncoder.getQrCodeBitMatrix()?.let { qrCodeEncoder.getQrCodeBitMatrix()?.let {
println(QrCodeRenderer.getQrString(it)) println(QrCodeRenderer.getQrString(it))
} }
......
...@@ -59,10 +59,14 @@ import javax.annotation.Nullable; ...@@ -59,10 +59,14 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import kotlinx.coroutines.flow.MutableStateFlow;
import kotlinx.coroutines.flow.StateFlow;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY; import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
import static org.briarproject.mailbox.core.tor.TorConstants.CONTROL_PORT; import static org.briarproject.mailbox.core.tor.TorConstants.CONTROL_PORT;
...@@ -70,7 +74,6 @@ import static org.briarproject.mailbox.core.tor.TorConstants.HS_ADDRESS_V3; ...@@ -70,7 +74,6 @@ import static org.briarproject.mailbox.core.tor.TorConstants.HS_ADDRESS_V3;
import static org.briarproject.mailbox.core.tor.TorConstants.HS_PRIVATE_KEY_V3; import static org.briarproject.mailbox.core.tor.TorConstants.HS_PRIVATE_KEY_V3;
import static org.briarproject.mailbox.core.tor.TorConstants.SETTINGS_NAMESPACE; import static org.briarproject.mailbox.core.tor.TorConstants.SETTINGS_NAMESPACE;
import static org.briarproject.mailbox.core.tor.TorPlugin.State.ACTIVE; import static org.briarproject.mailbox.core.tor.TorPlugin.State.ACTIVE;
import static org.briarproject.mailbox.core.tor.TorPlugin.State.DISABLED;
import static org.briarproject.mailbox.core.tor.TorPlugin.State.ENABLING; import static org.briarproject.mailbox.core.tor.TorPlugin.State.ENABLING;
import static org.briarproject.mailbox.core.tor.TorPlugin.State.INACTIVE; import static org.briarproject.mailbox.core.tor.TorPlugin.State.INACTIVE;
import static org.briarproject.mailbox.core.tor.TorPlugin.State.STARTING_STOPPING; import static org.briarproject.mailbox.core.tor.TorPlugin.State.STARTING_STOPPING;
...@@ -155,6 +158,10 @@ public abstract class TorPlugin ...@@ -155,6 +158,10 @@ public abstract class TorPlugin
return new File(torDirectory, "obfs4proxy"); return new File(torDirectory, "obfs4proxy");
} }
public StateFlow<State> getState() {
return state.state;
}
@Override @Override
public void startService() throws ServiceException { public void startService() throws ServiceException {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
...@@ -404,6 +411,7 @@ public abstract class TorPlugin ...@@ -404,6 +411,7 @@ public abstract class TorPlugin
logException(LOG, e); logException(LOG, e);
} }
} }
state.setServicePublished();
} }
@Nullable @Nullable
...@@ -534,7 +542,6 @@ public abstract class TorPlugin ...@@ -534,7 +542,6 @@ public abstract class TorPlugin
else LOG.info("Country code: " + country); else LOG.info("Country code: " + country);
} }
int reasonsDisabled = 0;
boolean enableNetwork = false; boolean enableNetwork = false;
boolean enableBridges = false; boolean enableBridges = false;
boolean useMeek = false; boolean useMeek = false;
...@@ -557,7 +564,6 @@ public abstract class TorPlugin ...@@ -557,7 +564,6 @@ public abstract class TorPlugin
LOG.info("Not using bridges"); LOG.info("Not using bridges");
} }
} }
state.setReasonsDisabled(reasonsDisabled);
try { try {
if (enableNetwork) { if (enableNetwork) {
enableBridges(enableBridges, useMeek); enableBridges(enableBridges, useMeek);
...@@ -581,7 +587,10 @@ public abstract class TorPlugin ...@@ -581,7 +587,10 @@ public abstract class TorPlugin
} }
@ThreadSafe @ThreadSafe
protected class PluginState { protected static class PluginState {
private final MutableStateFlow<State> state =
MutableStateFlow(STARTING_STOPPING);
@GuardedBy("this") @GuardedBy("this")
private boolean started = false, private boolean started = false,
...@@ -590,10 +599,7 @@ public abstract class TorPlugin ...@@ -590,10 +599,7 @@ public abstract class TorPlugin
networkEnabled = false, networkEnabled = false,
bootstrapped = false, bootstrapped = false,
circuitBuilt = false, circuitBuilt = false,
settingsChecked = false; servicePublished = false;
@GuardedBy("this")
private int reasonsDisabled = 0;
@GuardedBy("this") @GuardedBy("this")
@Nullable @Nullable
...@@ -601,7 +607,7 @@ public abstract class TorPlugin ...@@ -601,7 +607,7 @@ public abstract class TorPlugin
synchronized void setStarted() { synchronized void setStarted() {
started = true; started = true;
// callback.pluginStateChanged(getState()); state.setValue(getCurrentState());
} }
synchronized boolean isTorRunning() { synchronized boolean isTorRunning() {
...@@ -613,63 +619,52 @@ public abstract class TorPlugin ...@@ -613,63 +619,52 @@ public abstract class TorPlugin
stopped = true; stopped = true;
ServerSocket ss = serverSocket; ServerSocket ss = serverSocket;
serverSocket = null; serverSocket = null;
// callback.pluginStateChanged(getState()); state.setValue(getCurrentState());
return ss; return ss;
} }
synchronized void setBootstrapped() { synchronized void setBootstrapped() {
bootstrapped = true; bootstrapped = true;
// callback.pluginStateChanged(getState()); state.setValue(getCurrentState());
} }
synchronized boolean getAndSetCircuitBuilt() { synchronized boolean getAndSetCircuitBuilt() {
boolean firstCircuit = !circuitBuilt; boolean firstCircuit = !circuitBuilt;
circuitBuilt = true; circuitBuilt = true;
// callback.pluginStateChanged(getState()); state.setValue(getCurrentState());
return firstCircuit; return firstCircuit;
} }
synchronized void setServicePublished() {
servicePublished = true;
state.setValue(getCurrentState());
}
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;
// callback.pluginStateChanged(getState()); state.setValue(getCurrentState());
} }
synchronized void setReasonsDisabled(int reasonsDisabled) { private synchronized State getCurrentState() {
settingsChecked = true; if (!started || stopped) {
this.reasonsDisabled = reasonsDisabled;
// callback.pluginStateChanged(getState());
}
synchronized State getState() {
if (!started || stopped || !settingsChecked) {
return STARTING_STOPPING; return STARTING_STOPPING;
} }
if (reasonsDisabled != 0) return DISABLED;
if (!networkInitialised) return ENABLING; if (!networkInitialised) return ENABLING;
if (!networkEnabled) return INACTIVE; if (!networkEnabled) return INACTIVE;
return bootstrapped && circuitBuilt ? ACTIVE : ENABLING; return bootstrapped && circuitBuilt && servicePublished ?
} ACTIVE : ENABLING;
synchronized int getReasonsDisabled() {
return getState() == DISABLED ? reasonsDisabled : 0;
} }
} }
enum State { public enum State {
/** /**
* The plugin has not finished starting or has been stopped. * The plugin has not finished starting or has been stopped.
*/ */
STARTING_STOPPING, STARTING_STOPPING,
/**
* The plugin is disabled by settings.
*/
DISABLED,
/** /**
* The plugin is being enabled and can't yet make or receive * The plugin is being enabled and can't yet make or receive
* connections. * connections.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment