diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt index 2e3c5c404dd4c0e538867952ef785b5b0083e6b8..ae750a7966379ed6790f7fc5a314fc89888282cd 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt @@ -78,6 +78,10 @@ internal class AndroidTorModule { throw UnsupportedOperationException() } + override fun onSettingsChanged() { + throw UnsupportedOperationException() + } + override fun getHiddenServiceAddress(): String { throw UnsupportedOperationException() } diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/AbstractTorPlugin.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/AbstractTorPlugin.java index 8f2d8d3fbe424103a278af26adb4eb36efaf3d0f..f9bd904543619e9c5e4d6ddc3f15e9f73c5092bf 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/AbstractTorPlugin.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/AbstractTorPlugin.java @@ -54,14 +54,24 @@ import kotlinx.coroutines.flow.StateFlow; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_AUTO; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_MEEK; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_OBFS4; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_OBFS4_DEFAULT; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_SNOWFLAKE; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_VANILLA; 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.SETTINGS_NAMESPACE; import static org.briarproject.mailbox.core.util.LogUtils.info; import static org.briarproject.mailbox.core.util.LogUtils.logException; import static org.briarproject.mailbox.core.util.PrivacyUtils.scrubOnion; +import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.DEFAULT_OBFS4; import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.MEEK; +import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.SNOWFLAKE; +import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.VANILLA; import static org.slf4j.LoggerFactory.getLogger; public abstract class AbstractTorPlugin implements TorPlugin, EventListener { @@ -89,6 +99,8 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { protected final PluginState state = new PluginState(); + private volatile Settings settings = null; + AbstractTorPlugin(Executor ioExecutor, SettingsManager settingsManager, NetworkManager networkManager, @@ -139,6 +151,13 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { @Override public void startService() throws ServiceException { if (used.getAndSet(true)) throw new IllegalStateException(); + // Load the settings + try { + settings = settingsManager.getSettings(SETTINGS_NAMESPACE); + } catch (DbException e) { + logException(LOG, e, "Error while retrieving settings"); + settings = new Settings(); + } // Start Tor try { tor.start(); @@ -168,14 +187,7 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { private void publishHiddenService(int port) { if (!tor.isTorRunning()) return; - Settings s; - try { - s = settingsManager.getSettings(SETTINGS_NAMESPACE); - } catch (DbException e) { - logException(LOG, e, "Error while retrieving settings"); - s = new Settings(); - } - String privateKey3 = s.get(HS_PRIVATE_KEY_V3); + String privateKey3 = settings.get(HS_PRIVATE_KEY_V3); createV3HiddenService(port, privateKey3); } @@ -197,6 +209,8 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { s.put(HS_PRIVATE_KEY_V3, hsProps.privKey); try { settingsManager.mergeSettings(s, SETTINGS_NAMESPACE); + // update cached settings with merge result + settings = settingsManager.getSettings(SETTINGS_NAMESPACE); } catch (DbException e) { logException(LOG, e, "Error while merging settings"); } @@ -204,9 +218,8 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { } @Nullable - public String getHiddenServiceAddress() throws DbException { - Settings s = settingsManager.getSettings(SETTINGS_NAMESPACE); - return s.get(HS_ADDRESS_V3); + public String getHiddenServiceAddress() { + return settings == null ? null : settings.get(HS_ADDRESS_V3); } private void enableBridges(List<BridgeType> bridgeTypes, String countryCode) @@ -240,6 +253,20 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { } } + @Override + public void onSettingsChanged() { + ioExecutor.execute(() -> { + try { + settings = settingsManager.getSettings(SETTINGS_NAMESPACE); + } catch (DbException e) { + logException(LOG, e, "Error while retrieving settings"); + settings = new Settings(); + } + // TODO reset actual plugin state, if this causes us to lose conn + updateConnectionStatus(networkManager.getNetworkStatus()); + }); + } + private void updateConnectionStatus(NetworkStatus status) { connectionStatusExecutor.execute(() -> { if (!tor.isTorRunning()) return; @@ -247,7 +274,6 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { boolean wifi = status.isWifi(); boolean ipv6Only = status.isIpv6Only(); String country = locationUtils.getCurrentCountry(); - boolean bridgesWork = circumventionProvider.doBridgesWork(country); if (LOG.isInfoEnabled()) { LOG.info("Online: " + online + ", wifi: " + wifi @@ -264,19 +290,7 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { } else { LOG.info("Enabling network"); enableNetwork = true; - if (bridgesWork) { - if (ipv6Only) { - bridgeTypes = asList(MEEK, SNOWFLAKE); - } else { - bridgeTypes = circumventionProvider - .getSuitableBridgeTypes(country); - } - if (LOG.isInfoEnabled()) { - LOG.info("Using bridge types " + bridgeTypes); - } - } else { - LOG.info("Not using bridges"); - } + bridgeTypes = getBridgeTypes(country, ipv6Only); if (wifi) { LOG.info("Enabling connection padding"); enableConnectionPadding = true; @@ -298,6 +312,50 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { }); } + private List<BridgeType> getBridgeTypes(String country, boolean ipv6Only) { + List<BridgeType> bridgeTypes = emptyList(); + boolean bridgesNeeded = + circumventionProvider.doBridgesWork(country); + boolean bridgeAuto = settings.getBoolean(BRIDGE_AUTO, true); + if (bridgeAuto) { + if (bridgesNeeded) { + if (ipv6Only) { + bridgeTypes = asList(MEEK, SNOWFLAKE); + } else { + bridgeTypes = circumventionProvider + .getSuitableBridgeTypes(country); + } + if (LOG.isInfoEnabled()) { + LOG.info("Using bridge types " + bridgeTypes); + } + } else { + LOG.info("Not using bridges"); + } + } else { + boolean useBridges = settings.getBoolean(BRIDGE_USE, false); + if (useBridges) { + ArrayList<BridgeType> types = new ArrayList<>(); + if (settings.getBoolean(BRIDGE_USE_SNOWFLAKE, false)) + types.add(SNOWFLAKE); + if (settings.getBoolean(BRIDGE_USE_MEEK, false)) + types.add(MEEK); + if (settings.getBoolean(BRIDGE_USE_OBFS4, false)) + types.add(NON_DEFAULT_OBFS4); + if (settings.getBoolean(BRIDGE_USE_OBFS4_DEFAULT, false)) + types.add(DEFAULT_OBFS4); + if (settings.getBoolean(BRIDGE_USE_VANILLA, false)) + types.add(VANILLA); + bridgeTypes = types; + if (LOG.isInfoEnabled()) { + LOG.info("Using bridge types " + bridgeTypes); + } + } else { + LOG.info("Not using bridges"); + } + } + return bridgeTypes; + } + @ThreadSafe private class PluginState { diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorConstants.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorConstants.java index ca6b6633ecfe7eb554e85c5016f63bf91271f57d..cb463466749c52387e84c7f15191496a594e6057 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorConstants.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorConstants.java @@ -25,6 +25,20 @@ public interface TorConstants { String SETTINGS_NAMESPACE = "Tor"; String HS_PRIVATE_KEY_V3 = "onionPrivKey3"; String HS_ADDRESS_V3 = "onionAddress3"; + /** + * Whether circumvention bridge handling should be handled automatically. + */ + String BRIDGE_AUTO = "bridgeAuto"; + /** + * Whether bridges should be used for circumvention. + * Only consider when {@link #BRIDGE_AUTO} is false. + */ + String BRIDGE_USE = "bridgeUse"; + String BRIDGE_USE_SNOWFLAKE = "bridgeUseSnowflake"; + String BRIDGE_USE_MEEK = "bridgeUseMeek"; + String BRIDGE_USE_OBFS4 = "bridgeUseObfs4"; + String BRIDGE_USE_OBFS4_DEFAULT = "bridgeUseObfs4Default"; + String BRIDGE_USE_VANILLA = "bridgeUseVanilla"; int SOCKS_PORT = 59054; int CONTROL_PORT = 59055; diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java index bbc28f42779005a40bd06206cb6c34c8939a2c85..851ce75fbb249ecc244220d901aea960a1e7b9d2 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java @@ -2,6 +2,7 @@ package org.briarproject.mailbox.core.tor; import org.briarproject.mailbox.core.db.DbException; import org.briarproject.mailbox.core.lifecycle.Service; +import org.briarproject.mailbox.core.settings.Settings; import kotlinx.coroutines.flow.StateFlow; @@ -9,6 +10,16 @@ public interface TorPlugin extends Service { StateFlow<TorPluginState> getState(); + /** + * Call this whenever {@link Settings} in + * {@link TorConstants#SETTINGS_NAMESPACE} have changed. + */ + void onSettingsChanged(); + + /** + * This is only available after {@link #startService()} has returned. + * Otherwise returns null. + */ String getHiddenServiceAddress() throws DbException; } diff --git a/mailbox-lib/src/main/java/org/briarproject/mailbox/core/tor/FakeTorPlugin.kt b/mailbox-lib/src/main/java/org/briarproject/mailbox/core/tor/FakeTorPlugin.kt index d56ec5cfb1c9207dba95387effb3d229e71b87fc..95b2db49cca108e1a58b0ca329567b38b60dd5eb 100644 --- a/mailbox-lib/src/main/java/org/briarproject/mailbox/core/tor/FakeTorPlugin.kt +++ b/mailbox-lib/src/main/java/org/briarproject/mailbox/core/tor/FakeTorPlugin.kt @@ -20,5 +20,8 @@ class FakeTorPlugin @Inject constructor() : TorPlugin { return state } + override fun onSettingsChanged() { + } + override fun getHiddenServiceAddress(): String? = null }