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 7ecc2d7d2addb76e603f4a02583926dbd54f2ab4..50936b93197b13e5e3db8ede7498a4ef60179cac 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 @@ -11,6 +11,7 @@ public interface CircumventionProvider { enum BridgeType { DEFAULT_OBFS4, NON_DEFAULT_OBFS4, + VANILLA, MEEK } @@ -23,27 +24,27 @@ public interface CircumventionProvider { String[] BLOCKED = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"}; /** - * Countries where obfs4 or meek bridge connections are likely to work. + * Countries where bridge connections are likely to work. * Should be a subset of {@link #BLOCKED} and the union of - * {@link #DEFAULT_OBFS4_BRIDGES}, {@link #NON_DEFAULT_OBFS4_BRIDGES} and + * {@link #DEFAULT_BRIDGES}, {@link #NON_DEFAULT_BRIDGES} and * {@link #MEEK_BRIDGES}. */ String[] BRIDGES = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"}; /** - * Countries where default obfs4 bridges are likely to work. + * Countries where default obfs4 or vanilla bridges are likely to work. * Should be a subset of {@link #BRIDGES}. */ - String[] DEFAULT_OBFS4_BRIDGES = {"EG", "VE"}; + String[] DEFAULT_BRIDGES = {"EG", "VE"}; /** - * Countries where non-default obfs4 bridges are likely to work. + * Countries where non-default obfs4 or vanilla bridges are likely to work. * Should be a subset of {@link #BRIDGES}. */ - String[] NON_DEFAULT_OBFS4_BRIDGES = {"BY", "RU", "TM"}; + String[] NON_DEFAULT_BRIDGES = {"BY", "RU", "TM"}; /** - * Countries where obfs4 bridges won't work and meek is needed. + * Countries where obfs4 and vanilla bridges won't work and meek is needed. * Should be a subset of {@link #BRIDGES}. */ String[] MEEK_BRIDGES = {"CN", "IR"}; @@ -60,10 +61,11 @@ public interface CircumventionProvider { boolean doBridgesWork(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. + * Returns the types of bridge connection that are suitable for the given + * country, or {@link #DEFAULT_BRIDGES} if no bridge type is known + * to work. */ - BridgeType getBestBridgeType(String countryCode); + List<BridgeType> getSuitableBridgeTypes(String countryCode); @IoExecutor List<String> 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 c05e78bd035d86612df129f941dd8f347e6be62b..fae5044bca1763cd96f50b632d364c96d8f16c81 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 @@ -14,10 +14,12 @@ import javax.annotation.concurrent.Immutable; import javax.inject.Inject; import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; 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; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA; @Immutable @NotNullByDefault @@ -30,9 +32,9 @@ class CircumventionProviderImpl implements CircumventionProvider { private static final Set<String> BRIDGE_COUNTRIES = new HashSet<>(asList(BRIDGES)); private static final Set<String> DEFAULT_OBFS4_BRIDGE_COUNTRIES = - new HashSet<>(asList(DEFAULT_OBFS4_BRIDGES)); - private static final Set<String> NON_DEFAULT_OBFS4_BRIDGE_COUNTRIES = - new HashSet<>(asList(NON_DEFAULT_OBFS4_BRIDGES)); + new HashSet<>(asList(DEFAULT_BRIDGES)); + private static final Set<String> NON_DEFAULT_BRIDGE_COUNTRIES = + new HashSet<>(asList(NON_DEFAULT_BRIDGES)); private static final Set<String> MEEK_COUNTRIES = new HashSet<>(asList(MEEK_BRIDGES)); @@ -51,15 +53,15 @@ class CircumventionProviderImpl implements CircumventionProvider { } @Override - public BridgeType getBestBridgeType(String countryCode) { + public List<BridgeType> getSuitableBridgeTypes(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; + return asList(DEFAULT_OBFS4, VANILLA); + } else if (NON_DEFAULT_BRIDGE_COUNTRIES.contains(countryCode)) { + return asList(NON_DEFAULT_OBFS4, VANILLA); } else if (MEEK_COUNTRIES.contains(countryCode)) { - return MEEK; + return singletonList(MEEK); } else { - return DEFAULT_OBFS4; + return asList(DEFAULT_OBFS4, VANILLA); } } @@ -73,12 +75,10 @@ class CircumventionProviderImpl implements CircumventionProvider { List<String> bridges = new ArrayList<>(); while (scanner.hasNextLine()) { String line = scanner.nextLine(); - 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)) { + if ((type == DEFAULT_OBFS4 && line.startsWith("d ")) || + (type == NON_DEFAULT_OBFS4 && line.startsWith("n ")) || + (type == VANILLA && line.startsWith("v ")) || + (type == MEEK && line.startsWith("m "))) { bridges.add(line.substring(2)); } } 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 6e0cf315575a4c8f8128574ed76bcf94a6a0b2fc..aa0fff972a90f2660dd74fe9a28231aed91531e9 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 @@ -92,7 +92,9 @@ 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.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.TorRendezvousCrypto.SEED_BYTES; import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.tryToClose; @@ -541,20 +543,24 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); } - private void enableBridges(boolean enable, BridgeType bridgeType) + private void enableBridges(boolean enable, List<BridgeType> bridgeTypes) throws IOException { if (enable) { Collection<String> conf = new ArrayList<>(); conf.add("UseBridges 1"); File obfs4File = getObfs4ExecutableFile(); - if (bridgeType == MEEK) { + if (bridgeTypes.contains(MEEK)) { conf.add("ClientTransportPlugin meek_lite exec " + obfs4File.getAbsolutePath()); - } else { + } + if (bridgeTypes.contains(DEFAULT_OBFS4) || + bridgeTypes.contains(NON_DEFAULT_OBFS4)) { conf.add("ClientTransportPlugin obfs4 exec " + obfs4File.getAbsolutePath()); } - conf.addAll(circumventionProvider.getBridges(bridgeType)); + for (BridgeType bridgeType : bridgeTypes) { + conf.addAll(circumventionProvider.getBridges(bridgeType)); + } controlConnection.setConf(conf); } else { controlConnection.setConf("UseBridges", "0"); @@ -909,8 +915,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { int reasonsDisabled = 0; boolean enableNetwork = false, enableBridges = false; boolean enableConnectionPadding = false; - BridgeType bridgeType = - circumventionProvider.getBestBridgeType(country); + List<BridgeType> bridgeTypes = + circumventionProvider.getSuitableBridgeTypes(country); if (!online) { LOG.info("Disabling network, device is offline"); @@ -939,10 +945,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { enableNetwork = true; if (network == PREF_TOR_NETWORK_WITH_BRIDGES || (automatic && bridgesWork)) { - if (ipv6Only) bridgeType = MEEK; + if (ipv6Only) bridgeTypes = singletonList(MEEK); enableBridges = true; if (LOG.isLoggable(INFO)) { - LOG.info("Using bridge type " + bridgeType); + LOG.info("Using bridge types " + bridgeTypes); } } else { LOG.info("Not using bridges"); @@ -960,7 +966,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { try { if (enableNetwork) { - enableBridges(enableBridges, bridgeType); + enableBridges(enableBridges, bridgeTypes); enableConnectionPadding(enableConnectionPadding); useIpv6(ipv6Only); } diff --git a/bramble-core/src/main/resources/bridges b/bramble-core/src/main/resources/bridges index 180d16c2a06964e2413a828831371cfc37b7f24b..101dd1ddea1bee7cb3aabe78e8ebbc83b6f75d63 100644 --- a/bramble-core/src/main/resources/bridges +++ b/bramble-core/src/main/resources/bridges @@ -10,10 +10,17 @@ d Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cer d Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0 d Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0 n Bridge obfs4 46.226.107.197:10300 A38FD6BDFD902882F5F5B9B7CCC95602A20B0BC4 cert=t8tA9q2AeGlmp/dO6oW9bkY5RqqmvqjArCEM9wjJoDnk6XtnaejkF0JTA7VamdyOzcvuBg iat-mode=0 -n Bridge obfs4 23.88.49.56:443 1CDA1660823AE2565D7F50DE8EB99DFDDE96074B cert=4bwNXedHutVD0ZqCm6ph90Vik9dRY4n9qnBHiLiqQOSsIvui4iHwuMFQK6oqiK8tyhVcDw iat-mode=0 n Bridge obfs4 185.181.11.86:443 A961609729E7FDF520B4E81F1F1B8FA1045285C3 cert=e5faG9Zk4Ni+e7z2YgGfevyKPQlMvkVGi4ublSsHYjaBovKeNXpOhbeFxzbZZoAzxAoGUQ iat-mode=0 n Bridge obfs4 85.242.211.221:8042 A36A938DD7FDB8BACC846BA326EE0BA0D89A9252 cert=1AN6Pt1eFca3Y/WYD2TGAU3Al9cO4eouXE9SX63s66Z/ks3tVmgQ5GeXi1B5DOvx6Il7Zw iat-mode=0 -n Bridge obfs4 185.161.70.200:9003 7A81D0CD19870DFA3FD13C5DE232D8ADD026DC40 cert=aIcWxyZS3JcWsowlD9hcw9fttA44Bq/W2laFjVWlhuXqrIlAAwrXvq1O9lm9XrkV8GG/ZA iat-mode=0 n Bridge obfs4 172.105.22.69:80 CBD17B33192A879433AB37C9E142541BD3459ABD cert=rk5YmpKypLsjlS4tjkYaZNBweYMa5tWQRhZ8Q2WRleNOgrhSceKo59BA8kp6kVfaMPXnSw iat-mode=0 n Bridge obfs4 46.128.93.192:7346 5D28B8E1D117B8720D56A8513CF32509DCA1D84F cert=ED6tZP50eF0vno09F5gFvoWTMdcWFEX2FtwXOUYRevjzKg30/y701f61Vycnh6HO9gkaMw iat-mode=0 +n Bridge obfs4 74.104.165.202:9002 EF432018A6AA5D970B2F84E39CD30A147030141C cert=PhppfUusY85dHGvWtGTybZ1fED4DtbHmALkNMIOIYrAz1B4xN7/2a5gyiZe1epju1BOHVg iat-mode=0 +n Bridge obfs4 70.34.242.31:443 7F026956402CDFF4BCBA8E11EE9C50E3FE0A2B72 cert=hP/KU7JATSfWH3HwS5Er/YLT0J+bRO3+s2fWx2yirrgf37EyrWvm/BQshoNje8WfUm6CBw iat-mode=0 +n Bridge obfs4 192.3.163.88:57145 DEB62DE9643E5956CA4FA78035B48C9BBABE7F29 cert=RMz2z9uVVrioUhx0A8xUmiftRe2RpcXiqIuDfisdIomcHDf82nzfn83X/ixGUiA4rLCAdw iat-mode=0 +n Bridge obfs4 93.95.226.151:41185 460B0CFFC0CF1D965F3DE064E08BA1915E7C916A cert=inluPzp5Jp5OzZar1eQb4dcQ/YlAj/v0kHAUCoCr3rmLt03+pVuVTjoH4mRy4+acXpn+Gw iat-mode=0 +n Bridge obfs4 120.29.217.52:5223 40FE3DB9800272F9CDC76422F8ED7883280EE96D cert=/71PS4l8c/XJ4DIItlH9xMqNvPFg2RUTrHvPlQWh48u5et8h/yyyjCcYphUadDsfBWpaGQ iat-mode=0 +v Bridge 5.45.96.40:9001 8723B591712AAA03FB92000370BD356AB4997FA7 +v Bridge 135.181.113.164:54444 74AF4CCA614C454B7D3E81FF8BACD78CEBC7D7DE +v Bridge 152.44.197.85:10507 FF07DF6B4720DA4C50F1A025662D50916D6223F6 +v Bridge 209.216.78.21:443 C870D381E7264CDB83BAEEBF074804808CCCDB8D 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 index caecdd453f1e061c275b13e61b59db79b89ea28c..593275ed62803575e9339ffd4aaaff911192fa6f 100644 --- 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 @@ -7,14 +7,16 @@ import java.util.HashSet; import java.util.Set; import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; 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.BridgeType.VANILLA; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DEFAULT_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.briarproject.bramble.plugin.tor.CircumventionProvider.NON_DEFAULT_BRIDGES; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -27,37 +29,39 @@ public class CircumventionProviderTest extends BrambleTestCase { public void testInvariants() { Set<String> blocked = new HashSet<>(asList(BLOCKED)); Set<String> bridges = new HashSet<>(asList(BRIDGES)); - Set<String> defaultObfs4Bridges = - new HashSet<>(asList(DEFAULT_OBFS4_BRIDGES)); - Set<String> nonDefaultObfs4Bridges = - new HashSet<>(asList(NON_DEFAULT_OBFS4_BRIDGES)); + Set<String> defaultBridges = new HashSet<>(asList(DEFAULT_BRIDGES)); + Set<String> nonDefaultBridges = + new HashSet<>(asList(NON_DEFAULT_BRIDGES)); Set<String> 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<String> union = new HashSet<>(defaultObfs4Bridges); - union.addAll(nonDefaultObfs4Bridges); + Set<String> union = new HashSet<>(defaultBridges); + union.addAll(nonDefaultBridges); union.addAll(meekBridges); assertEquals(bridges, union); // The bridge type sets should not overlap - assertEmptyIntersection(defaultObfs4Bridges, nonDefaultObfs4Bridges); - assertEmptyIntersection(defaultObfs4Bridges, meekBridges); - assertEmptyIntersection(nonDefaultObfs4Bridges, meekBridges); + assertEmptyIntersection(defaultBridges, nonDefaultBridges); + assertEmptyIntersection(defaultBridges, meekBridges); + assertEmptyIntersection(nonDefaultBridges, meekBridges); } @Test public void testGetBestBridgeType() { - for (String country : DEFAULT_OBFS4_BRIDGES) { - assertEquals(DEFAULT_OBFS4, provider.getBestBridgeType(country)); + for (String country : DEFAULT_BRIDGES) { + assertEquals(asList(DEFAULT_OBFS4, VANILLA), + provider.getSuitableBridgeTypes(country)); } - for (String country : NON_DEFAULT_OBFS4_BRIDGES) { - assertEquals(NON_DEFAULT_OBFS4, - provider.getBestBridgeType(country)); + for (String country : NON_DEFAULT_BRIDGES) { + assertEquals(asList(NON_DEFAULT_OBFS4, VANILLA), + provider.getSuitableBridgeTypes(country)); } for (String country : MEEK_BRIDGES) { - assertEquals(MEEK, provider.getBestBridgeType(country)); + assertEquals(singletonList(MEEK), + provider.getSuitableBridgeTypes(country)); } - assertEquals(DEFAULT_OBFS4, provider.getBestBridgeType("ZZ")); + assertEquals(asList(DEFAULT_OBFS4, VANILLA), + provider.getSuitableBridgeTypes("ZZ")); } private <T> void assertEmptyIntersection(Set<T> a, Set<T> b) { 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 c5cc1f51fad98efd0b52ac9581a65e3f910fc7fa..bcbe3ce14fbbea8f78ca078c0de8fe2d617422aa 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 @@ -47,6 +47,7 @@ import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_POR 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.BridgeType.VANILLA; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled; @@ -74,6 +75,9 @@ public class BridgeTest extends BrambleTestCase { for (String bridge : provider.getBridges(NON_DEFAULT_OBFS4)) { states.add(new Params(bridge, NON_DEFAULT_OBFS4, stats, false)); } + for (String bridge : provider.getBridges(VANILLA)) { + states.add(new Params(bridge, VANILLA, stats, false)); + } for (String bridge : provider.getBridges(MEEK)) { states.add(new Params(bridge, MEEK, stats, true)); } @@ -83,7 +87,7 @@ public class BridgeTest extends BrambleTestCase { private final static long OBFS4_TIMEOUT = MINUTES.toMillis(2); private final static long MEEK_TIMEOUT = MINUTES.toMillis(6); - private final static int UNREACHABLE_BRIDGES_ALLOWED = 4; + private final static int UNREACHABLE_BRIDGES_ALLOWED = 6; private final static int ATTEMPTS_PER_BRIDGE = 5; private final static Logger LOG = getLogger(BridgeTest.class.getName()); @@ -99,8 +103,6 @@ public class BridgeTest extends BrambleTestCase { @Inject ResourceProvider resourceProvider; @Inject - CircumventionProvider circumventionProvider; - @Inject BatteryManager batteryManager; @Inject EventBus eventBus; @@ -150,8 +152,8 @@ public class BridgeTest extends BrambleTestCase { } @Override - public BridgeType getBestBridgeType(String countryCode) { - return params.bridgeType; + public List<BridgeType> getSuitableBridgeTypes(String countryCode) { + return singletonList(params.bridgeType); } @Override