From fa0610fff18e0d302450bbb47996c4cb724a736b Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Wed, 30 Mar 2022 14:49:03 +0100
Subject: [PATCH] Use vanilla bridges in parallel with obfs4 bridges.

---
 .../plugin/tor/CircumventionProvider.java     | 22 +++++-----
 .../plugin/tor/CircumventionProviderImpl.java | 30 +++++++-------
 .../bramble/plugin/tor/TorPlugin.java         | 24 ++++++-----
 bramble-core/src/main/resources/bridges       |  4 ++
 .../plugin/tor/CircumventionProviderTest.java | 40 ++++++++++---------
 .../bramble/plugin/tor/BridgeTest.java        | 10 +++--
 6 files changed, 74 insertions(+), 56 deletions(-)

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 7ecc2d7d2a..50936b9319 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 c05e78bd03..fae5044bca 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 e71a9107ce..d23d328d07 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;
@@ -528,20 +530,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");
@@ -831,8 +837,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");
@@ -861,10 +867,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");
@@ -882,7 +888,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 a4f9d1e35a..101dd1ddea 100644
--- a/bramble-core/src/main/resources/bridges
+++ b/bramble-core/src/main/resources/bridges
@@ -19,4 +19,8 @@ n Bridge obfs4 70.34.242.31:443 7F026956402CDFF4BCBA8E11EE9C50E3FE0A2B72 cert=hP
 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 caecdd453f..593275ed62 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 29adedc3a7..bcbe3ce14f 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));
 			}
@@ -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
-- 
GitLab