From 63c02100474751d7f091373c4e8039d02c4c3262 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 27 May 2022 16:33:09 +0100 Subject: [PATCH] Add Tor reachability monitor. --- .../mailbox/TorReachabilityMonitor.java | 45 ++++++ .../mailbox/TorReachabilityMonitorImpl.java | 128 ++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 bramble-core/src/main/java/org/briarproject/bramble/mailbox/TorReachabilityMonitor.java create mode 100644 bramble-core/src/main/java/org/briarproject/bramble/mailbox/TorReachabilityMonitorImpl.java diff --git a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/TorReachabilityMonitor.java b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/TorReachabilityMonitor.java new file mode 100644 index 000000000..e9620c9bd --- /dev/null +++ b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/TorReachabilityMonitor.java @@ -0,0 +1,45 @@ +package org.briarproject.bramble.mailbox; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.Plugin; + +import javax.annotation.concurrent.ThreadSafe; + +import static java.util.concurrent.TimeUnit.MINUTES; + +@ThreadSafe +@NotNullByDefault +interface TorReachabilityMonitor { + + /** + * How long the Tor plugin needs to be continuously + * {@link Plugin.State#ACTIVE active} before we assume our contacts can + * reach our hidden service. + */ + long REACHABILITY_PERIOD_MS = MINUTES.toMillis(10); + + /** + * Starts the monitor. + */ + void start(); + + /** + * Destroys the monitor. + */ + void destroy(); + + /** + * Adds an observer that will be called when our Tor hidden service becomes + * reachable. If our hidden service is already reachable, the observer is + * called immediately. + *

+ * Observers are removed after being called, or when the monitor is + * {@link #destroy() destroyed}. + */ + void addObserver(TorReachabilityObserver o); + + interface TorReachabilityObserver { + + void onTorReachable(); + } +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/TorReachabilityMonitorImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/TorReachabilityMonitorImpl.java new file mode 100644 index 000000000..a82d1a966 --- /dev/null +++ b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/TorReachabilityMonitorImpl.java @@ -0,0 +1,128 @@ +package org.briarproject.bramble.mailbox; + +import org.briarproject.bramble.api.Cancellable; +import org.briarproject.bramble.api.event.Event; +import org.briarproject.bramble.api.event.EventBus; +import org.briarproject.bramble.api.event.EventListener; +import org.briarproject.bramble.api.lifecycle.IoExecutor; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.Plugin; +import org.briarproject.bramble.api.plugin.PluginManager; +import org.briarproject.bramble.api.plugin.event.TransportActiveEvent; +import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; +import org.briarproject.bramble.api.system.TaskScheduler; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; +import javax.inject.Inject; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; +import static org.briarproject.bramble.api.plugin.TorConstants.ID; + +@ThreadSafe +@NotNullByDefault +class TorReachabilityMonitorImpl + implements TorReachabilityMonitor, EventListener { + + private final Executor ioExecutor; + private final TaskScheduler taskScheduler; + private final PluginManager pluginManager; + private final EventBus eventBus; + private final Object lock = new Object(); + + @GuardedBy("lock") + private boolean reachable = false, destroyed = false; + + @GuardedBy("lock") + private final List observers = new ArrayList<>(); + + @GuardedBy("lock") + @Nullable + private Cancellable task = null; + + @Inject + TorReachabilityMonitorImpl( + @IoExecutor Executor ioExecutor, + TaskScheduler taskScheduler, + PluginManager pluginManager, + EventBus eventBus) { + this.ioExecutor = ioExecutor; + this.taskScheduler = taskScheduler; + this.pluginManager = pluginManager; + this.eventBus = eventBus; + } + + @Override + public void start() { + eventBus.addListener(this); + Plugin plugin = pluginManager.getPlugin(ID); + if (plugin != null && plugin.getState() == ACTIVE) onTorActive(); + } + + @Override + public void destroy() { + eventBus.removeListener(this); + synchronized (lock) { + destroyed = true; + onTorInactive(); + observers.clear(); + } + } + + @Override + public void addObserver(TorReachabilityObserver o) { + boolean callNow = false; + synchronized (lock) { + if (destroyed) return; + if (reachable) callNow = true; + else observers.add(o); + } + if (callNow) o.onTorReachable(); + } + + @Override + public void eventOccurred(Event e) { + if (e instanceof TransportActiveEvent) { + TransportActiveEvent t = (TransportActiveEvent) e; + if (t.getTransportId().equals(ID)) onTorActive(); + } else if (e instanceof TransportInactiveEvent) { + TransportInactiveEvent t = (TransportInactiveEvent) e; + if (t.getTransportId().equals(ID)) onTorInactive(); + } + } + + private void onTorActive() { + synchronized (lock) { + if (destroyed || task != null) return; + task = taskScheduler.schedule(this::onTorReachable, ioExecutor, + REACHABILITY_PERIOD_MS, MILLISECONDS); + } + } + + private void onTorInactive() { + synchronized (lock) { + reachable = false; + if (task != null) task.cancel(); + task = null; + } + } + + @IoExecutor + private void onTorReachable() { + List observers; + synchronized (lock) { + if (destroyed) return; + reachable = true; + observers = new ArrayList<>(this.observers); + this.observers.clear(); + task = null; + } + for (TorReachabilityObserver o : observers) o.onTorReachable(); + } +}