diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java index 944399799..db766324a 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java @@ -1,40 +1,71 @@ package org.briarproject.bramble.plugin.tor; import org.briarproject.bramble.api.lifecycle.IoExecutor; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import java.util.List; -// TODO: Create a module for this so it doesn't have to be public - +@NotNullByDefault public interface CircumventionProvider { + enum BridgeType { + DEFAULT_OBFS4, + NON_DEFAULT_OBFS4, + MEEK + } + /** * Countries where Tor is blocked, i.e. vanilla Tor connection won't work. - * + *

* See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 * and https://trac.torproject.org/projects/tor/wiki/doc/OONI/censorshipwiki */ - String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE"}; + String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE", "RU"}; /** * Countries where obfs4 or meek bridge connections are likely to work. - * Should be a subset of {@link #BLOCKED}. + * Should be a subset of {@link #BLOCKED} and the union of + * {@link #DEFAULT_OBFS4_BRIDGES}, {@link #NON_DEFAULT_OBFS4_BRIDGES} and + * {@link #MEEK_BRIDGES}. */ - String[] BRIDGES = { "CN", "IR", "EG", "BY", "TR", "SY", "VE" }; + String[] BRIDGES = {"CN", "IR", "EG", "BY", "TR", "SY", "VE", "RU"}; + + /** + * Countries where default obfs4 bridges are likely to work. + * Should be a subset of {@link #BRIDGES}. + */ + String[] DEFAULT_OBFS4_BRIDGES = {"EG", "BY", "TR", "SY", "VE"}; + + /** + * Countries where non-default obfs4 bridges are likely to work. + * Should be a subset of {@link #BRIDGES}. + */ + String[] NON_DEFAULT_OBFS4_BRIDGES = {"RU"}; /** * Countries where obfs4 bridges won't work and meek is needed. * Should be a subset of {@link #BRIDGES}. */ - String[] NEEDS_MEEK = {"CN", "IR"}; + String[] MEEK_BRIDGES = {"CN", "IR"}; + /** + * Returns true if vanilla Tor connections are blocked in the given country. + */ boolean isTorProbablyBlocked(String countryCode); + /** + * Returns true if bridge connections of some type work in the given + * country. + */ boolean doBridgesWork(String countryCode); - boolean needsMeek(String countryCode); + /** + * Returns the best type of bridge connection for the given country, or + * {@link #DEFAULT_OBFS4_BRIDGES} if no bridge type is known to work. + */ + BridgeType getBestBridgeType(String countryCode); @IoExecutor - List getBridges(boolean meek); + List getBridges(BridgeType type); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java index 2e8c8ad90..c05e78bd0 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java @@ -1,6 +1,7 @@ package org.briarproject.bramble.plugin.tor; import org.briarproject.bramble.api.lifecycle.IoExecutor; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import java.io.InputStream; import java.util.ArrayList; @@ -9,24 +10,31 @@ import java.util.List; import java.util.Scanner; import java.util.Set; -import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; import javax.inject.Inject; import static java.util.Arrays.asList; +import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; +@Immutable +@NotNullByDefault class CircumventionProviderImpl implements CircumventionProvider { private final static String BRIDGE_FILE_NAME = "bridges"; private static final Set BLOCKED_IN_COUNTRIES = new HashSet<>(asList(BLOCKED)); - private static final Set BRIDGES_WORK_IN_COUNTRIES = + private static final Set BRIDGE_COUNTRIES = new HashSet<>(asList(BRIDGES)); - private static final Set BRIDGES_NEED_MEEK = - new HashSet<>(asList(NEEDS_MEEK)); - - @Nullable - private volatile List bridges = null; + private static final Set DEFAULT_OBFS4_BRIDGE_COUNTRIES = + new HashSet<>(asList(DEFAULT_OBFS4_BRIDGES)); + private static final Set NON_DEFAULT_OBFS4_BRIDGE_COUNTRIES = + new HashSet<>(asList(NON_DEFAULT_OBFS4_BRIDGES)); + private static final Set MEEK_COUNTRIES = + new HashSet<>(asList(MEEK_BRIDGES)); @Inject CircumventionProviderImpl() { @@ -39,33 +47,42 @@ class CircumventionProviderImpl implements CircumventionProvider { @Override public boolean doBridgesWork(String countryCode) { - return BRIDGES_WORK_IN_COUNTRIES.contains(countryCode); + return BRIDGE_COUNTRIES.contains(countryCode); } @Override - public boolean needsMeek(String countryCode) { - return BRIDGES_NEED_MEEK.contains(countryCode); + public BridgeType getBestBridgeType(String countryCode) { + if (DEFAULT_OBFS4_BRIDGE_COUNTRIES.contains(countryCode)) { + return DEFAULT_OBFS4; + } else if (NON_DEFAULT_OBFS4_BRIDGE_COUNTRIES.contains(countryCode)) { + return NON_DEFAULT_OBFS4; + } else if (MEEK_COUNTRIES.contains(countryCode)) { + return MEEK; + } else { + return DEFAULT_OBFS4; + } } @Override @IoExecutor - public List getBridges(boolean useMeek) { - List bridges = this.bridges; - if (bridges != null) return new ArrayList<>(bridges); - - InputStream is = getClass().getClassLoader() - .getResourceAsStream(BRIDGE_FILE_NAME); + public List getBridges(BridgeType type) { + InputStream is = requireNonNull(getClass().getClassLoader() + .getResourceAsStream(BRIDGE_FILE_NAME)); Scanner scanner = new Scanner(is); - bridges = new ArrayList<>(); + List bridges = new ArrayList<>(); while (scanner.hasNextLine()) { String line = scanner.nextLine(); - boolean isMeekBridge = line.startsWith("Bridge meek"); - if (useMeek && !isMeekBridge || !useMeek && isMeekBridge) continue; - if (!line.startsWith("#")) bridges.add(line); + boolean isDefaultObfs4 = line.startsWith("d "); + boolean isNonDefaultObfs4 = line.startsWith("n "); + boolean isMeek = line.startsWith("m "); + if ((type == DEFAULT_OBFS4 && isDefaultObfs4) || + (type == NON_DEFAULT_OBFS4 && isNonDefaultObfs4) || + (type == MEEK && isMeek)) { + bridges.add(line.substring(2)); + } } scanner.close(); - this.bridges = bridges; return bridges; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index 35d6548eb..f443b01bd 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -33,6 +33,7 @@ import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.ResourceProvider; +import org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType; import java.io.ByteArrayInputStream; import java.io.EOFException; @@ -92,6 +93,7 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK; import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES; import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.tryToClose; @@ -542,20 +544,20 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); } - private void enableBridges(boolean enable, boolean needsMeek) + private void enableBridges(boolean enable, BridgeType bridgeType) throws IOException { if (enable) { Collection conf = new ArrayList<>(); conf.add("UseBridges 1"); File obfs4File = getObfs4ExecutableFile(); - if (needsMeek) { + if (bridgeType == MEEK) { conf.add("ClientTransportPlugin meek_lite exec " + obfs4File.getAbsolutePath()); } else { conf.add("ClientTransportPlugin obfs4 exec " + obfs4File.getAbsolutePath()); } - conf.addAll(circumventionProvider.getBridges(needsMeek)); + conf.addAll(circumventionProvider.getBridges(bridgeType)); controlConnection.setConf(conf); } else { controlConnection.setConf("UseBridges", "0"); @@ -844,7 +846,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { int reasonsDisabled = 0; boolean enableNetwork = false, enableBridges = false; - boolean useMeek = false, enableConnectionPadding = false; + boolean enableConnectionPadding = false; + BridgeType bridgeType = + circumventionProvider.getBestBridgeType(country); if (!online) { LOG.info("Disabling network, device is offline"); @@ -873,14 +877,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { enableNetwork = true; if (network == PREF_TOR_NETWORK_WITH_BRIDGES || (automatic && bridgesWork)) { - if (ipv6Only || - circumventionProvider.needsMeek(country)) { - LOG.info("Using meek bridges"); - enableBridges = true; - useMeek = true; - } else { - LOG.info("Using obfs4 bridges"); - enableBridges = true; + if (ipv6Only) bridgeType = MEEK; + enableBridges = true; + if (LOG.isLoggable(INFO)) { + LOG.info("Using bridge type " + bridgeType); } } else { LOG.info("Not using bridges"); @@ -898,7 +898,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { try { if (enableNetwork) { - enableBridges(enableBridges, useMeek); + enableBridges(enableBridges, bridgeType); enableConnectionPadding(enableConnectionPadding); useIpv6(ipv6Only); } diff --git a/bramble-core/src/main/resources/bridges b/bramble-core/src/main/resources/bridges index 73188eba1..68e3e8e71 100644 --- a/bramble-core/src/main/resources/bridges +++ b/bramble-core/src/main/resources/bridges @@ -1,10 +1,17 @@ -Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0 -Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0 -Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0 -Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0 -Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0 -Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0 -Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0 -Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0 -Bridge obfs4 78.46.188.239:37356 5A2D2F4158D0453E00C7C176978D3F41D69C45DB cert=3c0SwxpOisbohNxEc4tb875RVW8eOu1opRTVXJhafaKA/PNNtI7ElQIVOVZg1AdL5bxGCw iat-mode=0 -Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com \ No newline at end of file +d Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1 +d Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0 +d Bridge obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0 +d Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0 +d Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0 +d Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0 +d Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0 +d Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0 +d Bridge obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0 +d Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0 +d Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0 +d Bridge obfs4 185.100.85.3:443 5B403DFE34F4872EB027059CECAE30B0C864B3A2 cert=bWUdFUe8io9U6JkSLoGAvSAUDcB779/shovCYmYAQb/pW/iEAMZtO/lCd94OokOF909TPA iat-mode=2 +n Bridge obfs4 46.226.107.197:10300 A38FD6BDFD902882F5F5B9B7CCC95602A20B0BC4 cert=t8tA9q2AeGlmp/dO6oW9bkY5RqqmvqjArCEM9wjJoDnk6XtnaejkF0JTA7VamdyOzcvuBg iat-mode=0 +n Bridge obfs4 74.104.165.202:9002 EF432018A6AA5D970B2F84E39CD30A147030141C cert=PhppfUusY85dHGvWtGTybZ1fED4DtbHmALkNMIOIYrAz1B4xN7/2a5gyiZe1epju1BOHVg iat-mode=0 +n Bridge obfs4 23.88.49.56:443 1CDA1660823AE2565D7F50DE8EB99DFDDE96074B cert=4bwNXedHutVD0ZqCm6ph90Vik9dRY4n9qnBHiLiqQOSsIvui4iHwuMFQK6oqiK8tyhVcDw iat-mode=0 +n Bridge obfs4 185.65.206.101:443 8A3E001D4C5105ED41060597DEEB21FF19CDC4D3 cert=Nd6XZ+f00sGKL1u6USmyvfqR34HN/pt7jEVbgMpXPF/yyGaLBiXRH/x0SIjX5TceYnd+Dg iat-mode=0 +m Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com \ No newline at end of file diff --git a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tor/CircumventionProviderTest.java b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tor/CircumventionProviderTest.java new file mode 100644 index 000000000..caecdd453 --- /dev/null +++ b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tor/CircumventionProviderTest.java @@ -0,0 +1,68 @@ +package org.briarproject.bramble.plugin.tor; + +import org.briarproject.bramble.test.BrambleTestCase; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static java.util.Arrays.asList; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BLOCKED; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BRIDGES; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DEFAULT_OBFS4_BRIDGES; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.MEEK_BRIDGES; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.NON_DEFAULT_OBFS4_BRIDGES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class CircumventionProviderTest extends BrambleTestCase { + + private final CircumventionProvider provider = + new CircumventionProviderImpl(); + + @Test + public void testInvariants() { + Set blocked = new HashSet<>(asList(BLOCKED)); + Set bridges = new HashSet<>(asList(BRIDGES)); + Set defaultObfs4Bridges = + new HashSet<>(asList(DEFAULT_OBFS4_BRIDGES)); + Set nonDefaultObfs4Bridges = + new HashSet<>(asList(NON_DEFAULT_OBFS4_BRIDGES)); + Set meekBridges = new HashSet<>(asList(MEEK_BRIDGES)); + // BRIDGES should be a subset of BLOCKED + assertTrue(blocked.containsAll(bridges)); + // BRIDGES should be the union of the bridge type sets + Set union = new HashSet<>(defaultObfs4Bridges); + union.addAll(nonDefaultObfs4Bridges); + union.addAll(meekBridges); + assertEquals(bridges, union); + // The bridge type sets should not overlap + assertEmptyIntersection(defaultObfs4Bridges, nonDefaultObfs4Bridges); + assertEmptyIntersection(defaultObfs4Bridges, meekBridges); + assertEmptyIntersection(nonDefaultObfs4Bridges, meekBridges); + } + + @Test + public void testGetBestBridgeType() { + for (String country : DEFAULT_OBFS4_BRIDGES) { + assertEquals(DEFAULT_OBFS4, provider.getBestBridgeType(country)); + } + for (String country : NON_DEFAULT_OBFS4_BRIDGES) { + assertEquals(NON_DEFAULT_OBFS4, + provider.getBestBridgeType(country)); + } + for (String country : MEEK_BRIDGES) { + assertEquals(MEEK, provider.getBestBridgeType(country)); + } + assertEquals(DEFAULT_OBFS4, provider.getBestBridgeType("ZZ")); + } + + private void assertEmptyIntersection(Set a, Set b) { + Set intersection = new HashSet<>(a); + intersection.retainAll(b); + assertTrue(intersection.isEmpty()); + } +} diff --git a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java index 04031f8c3..a8f76d91a 100644 --- a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java +++ b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java @@ -6,12 +6,14 @@ import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.network.NetworkManager; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.WakefulIoExecutor; +import org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType; import org.briarproject.bramble.test.BrambleJavaIntegrationTestComponent; import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.DaggerBrambleJavaIntegrationTestComponent; @@ -34,11 +36,14 @@ import javax.inject.Inject; import javax.net.SocketFactory; import static java.util.Collections.singletonList; -import static java.util.concurrent.TimeUnit.SECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled; @@ -57,14 +62,21 @@ public class BridgeTest extends BrambleTestCase { .injectEagerSingletons(component); // Share a failure counter among all the test instances AtomicInteger failures = new AtomicInteger(0); - List bridges = - component.getCircumventionProvider().getBridges(false); - List states = new ArrayList<>(bridges.size()); - for (String bridge : bridges) states.add(new Params(bridge, failures)); + CircumventionProvider provider = component.getCircumventionProvider(); + List states = new ArrayList<>(); + for (String bridge : provider.getBridges(DEFAULT_OBFS4)) { + states.add(new Params(bridge, DEFAULT_OBFS4, failures, false)); + } + for (String bridge : provider.getBridges(NON_DEFAULT_OBFS4)) { + states.add(new Params(bridge, NON_DEFAULT_OBFS4, failures, false)); + } + for (String bridge : provider.getBridges(MEEK)) { + states.add(new Params(bridge, MEEK, failures, true)); + } return states; } - private final static long TIMEOUT = SECONDS.toMillis(60); + private final static long TIMEOUT = MINUTES.toMillis(5); private final static int NUM_FAILURES_ALLOWED = 1; private final static Logger LOG = getLogger(BridgeTest.class.getName()); @@ -93,14 +105,12 @@ public class BridgeTest extends BrambleTestCase { CryptoComponent crypto; private final File torDir = getTestDirectory(); - private final String bridge; - private final AtomicInteger failures; + private final Params params; private UnixTorPluginFactory factory; public BridgeTest(Params params) { - bridge = params.bridge; - failures = params.failures; + this.params = params; } @Before @@ -120,6 +130,7 @@ public class BridgeTest extends BrambleTestCase { LocationUtils locationUtils = () -> "US"; SocketFactory torSocketFactory = SocketFactory.getDefault(); + @NotNullByDefault CircumventionProvider bridgeProvider = new CircumventionProvider() { @Override public boolean isTorProbablyBlocked(String countryCode) { @@ -132,13 +143,13 @@ public class BridgeTest extends BrambleTestCase { } @Override - public boolean needsMeek(String countryCode) { - return false; + public BridgeType getBestBridgeType(String countryCode) { + return params.bridgeType; } @Override - public List getBridges(boolean useMeek) { - return singletonList(bridge); + public List getBridges(BridgeType bridgeType) { + return singletonList(params.bridge); } }; factory = new UnixTorPluginFactory(ioExecutor, wakefulIoExecutor, @@ -160,7 +171,7 @@ public class BridgeTest extends BrambleTestCase { assertNotNull(duplexPlugin); UnixTorPlugin plugin = (UnixTorPlugin) duplexPlugin; - LOG.warning("Testing " + bridge); + LOG.warning("Testing " + params.bridge); try { plugin.start(); long start = clock.currentTimeMillis(); @@ -170,8 +181,11 @@ public class BridgeTest extends BrambleTestCase { } if (plugin.getState() != ACTIVE) { LOG.warning("Could not connect to Tor within timeout"); - if (failures.incrementAndGet() > NUM_FAILURES_ALLOWED) { - fail(failures.get() + " bridges are unreachable"); + if (params.failures.incrementAndGet() > NUM_FAILURES_ALLOWED) { + fail(params.failures.get() + " bridges are unreachable"); + } + if (params.mustSucceed) { + fail("essential bridge is unreachable"); } } } finally { @@ -182,11 +196,16 @@ public class BridgeTest extends BrambleTestCase { private static class Params { private final String bridge; + private final BridgeType bridgeType; private final AtomicInteger failures; + private final boolean mustSucceed; - private Params(String bridge, AtomicInteger failures) { + private Params(String bridge, BridgeType bridgeType, + AtomicInteger failures, boolean mustSucceed) { this.bridge = bridge; + this.bridgeType = bridgeType; this.failures = failures; + this.mustSucceed = mustSucceed; } } }