diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorPlugin.java b/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorPlugin.java index 92992fb3edf8dc2622296c583d6e92b93d124629..95ab9f408488b422bd84e743b120beb7a8a9b7de 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorPlugin.java +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorPlugin.java @@ -48,6 +48,8 @@ import java.util.zip.ZipInputStream; import javax.annotation.Nullable; +import androidx.annotation.ChecksSdkIntAtLeast; + import static android.os.Build.VERSION.SDK_INT; import static java.util.Arrays.asList; import static org.briarproject.mailbox.core.util.LogUtils.info; @@ -60,12 +62,13 @@ public class AndroidTorPlugin extends AbstractTorPlugin { private static final String TOR_LIB_NAME = "libtor.so"; private static final String OBFS4_LIB_NAME = "libobfs4proxy.so"; + private static final String SNOWFLAKE_LIB_NAME = "libsnowflake.so"; private static final Logger LOG = getLogger(AndroidTorPlugin.class); private final Context ctx; private final AndroidWakeLock wakeLock; - private final File torLib, obfs4Lib; + private final File torLib, obfs4Lib, snowflakeLib; AndroidTorPlugin(Executor ioExecutor, Context ctx, @@ -87,6 +90,7 @@ public class AndroidTorPlugin extends AbstractTorPlugin { String nativeLibDir = ctx.getApplicationInfo().nativeLibraryDir; torLib = new File(nativeLibDir, TOR_LIB_NAME); obfs4Lib = new File(nativeLibDir, OBFS4_LIB_NAME); + snowflakeLib = new File(nativeLibDir, SNOWFLAKE_LIB_NAME); } @Override @@ -112,6 +116,12 @@ public class AndroidTorPlugin extends AbstractTorPlugin { if (!enable) wakeLock.release(); } + @Override + @ChecksSdkIntAtLeast(api = 25) + protected boolean canVerifyLetsEncryptCerts() { + return SDK_INT >= 25; + } + @Override public void stopService() { super.stopService(); @@ -128,39 +138,43 @@ public class AndroidTorPlugin extends AbstractTorPlugin { return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile(); } + @Override + protected File getSnowflakeExecutableFile() { + return snowflakeLib.exists() + ? snowflakeLib : super.getSnowflakeExecutableFile(); + } + @Override protected void installTorExecutable() throws IOException { - File extracted = super.getTorExecutableFile(); - if (torLib.exists()) { - // If an older version left behind a Tor binary, delete it - if (extracted.exists()) { - if (extracted.delete()) LOG.info("Deleted Tor binary"); - else LOG.info("Failed to delete Tor binary"); - } - } else if (SDK_INT < 29) { - // The binary wasn't extracted at install time. Try to extract it - extractLibraryFromApk(TOR_LIB_NAME, extracted); - } else { - // No point extracting the binary, we won't be allowed to execute it - throw new FileNotFoundException(torLib.getAbsolutePath()); - } + installExecutable(super.getTorExecutableFile(), torLib, TOR_LIB_NAME); } @Override protected void installObfs4Executable() throws IOException { - File extracted = super.getObfs4ExecutableFile(); - if (obfs4Lib.exists()) { - // If an older version left behind an obfs4 binary, delete it + installExecutable(super.getObfs4ExecutableFile(), obfs4Lib, + OBFS4_LIB_NAME); + } + + @Override + protected void installSnowflakeExecutable() throws IOException { + installExecutable(super.getSnowflakeExecutableFile(), snowflakeLib, + SNOWFLAKE_LIB_NAME); + } + + private void installExecutable(File extracted, File lib, String libName) + throws IOException { + if (lib.exists()) { + // If an older version left behind a binary, delete it if (extracted.exists()) { - if (extracted.delete()) LOG.info("Deleted obfs4 binary"); - else LOG.info("Failed to delete obfs4 binary"); + if (extracted.delete()) LOG.info("Deleted old binary"); + else LOG.info("Failed to delete old binary"); } } else if (SDK_INT < 29) { // The binary wasn't extracted at install time. Try to extract it - extractLibraryFromApk(OBFS4_LIB_NAME, extracted); + extractLibraryFromApk(libName, extracted); } else { // No point extracting the binary, we won't be allowed to execute it - throw new FileNotFoundException(obfs4Lib.getAbsolutePath()); + throw new FileNotFoundException(lib.getAbsolutePath()); } } 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 c0c3b0a55189b9b78f159caec99d03701e8cd468..88166a3f30312a09cd32fbf5ff82f0f8b0a0bef8 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 @@ -476,7 +476,7 @@ public abstract class AbstractTorPlugin controlConnection.setConf("DisableNetwork", enable ? "0" : "1"); } - private void enableBridges(List<BridgeType> bridgeTypes) + private void enableBridges(List<BridgeType> bridgeTypes, String countryCode) throws IOException { if (!state.setBridgeTypes(bridgeTypes)) return; // Unchanged if (bridgeTypes.isEmpty()) { @@ -485,13 +485,24 @@ public abstract class AbstractTorPlugin } else { Collection<String> conf = new ArrayList<>(); conf.add("UseBridges 1"); + boolean letsEncrypt = canVerifyLetsEncryptCerts(); for (BridgeType bridgeType : bridgeTypes) { - conf.addAll(circumventionProvider.getBridges(bridgeType)); + conf.addAll(circumventionProvider + .getBridges(bridgeType, countryCode, letsEncrypt)); } controlConnection.setConf(conf); } } + /** + * Returns true if this device can verify Let's Encrypt certificates signed + * with the IdentTrust DST Root X3 certificate, which expired at the end of + * September 2021. + */ + protected boolean canVerifyLetsEncryptCerts() { + return true; + } + @Override public void stopService() { state.setStopped(); @@ -677,7 +688,7 @@ public abstract class AbstractTorPlugin try { if (enableNetwork) { - enableBridges(bridgeTypes); + enableBridges(bridgeTypes, country); enableConnectionPadding(enableConnectionPadding); enableIpv6(ipv6Only); } diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/CircumventionProvider.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/CircumventionProvider.java index b2e7685caa1c8bc747afc6bc09260c5d79815ab7..1978eca5cbf42a7469848ad0afb71ceaf2cb963f 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/CircumventionProvider.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/CircumventionProvider.java @@ -20,21 +20,32 @@ package org.briarproject.mailbox.core.tor; import org.briarproject.mailbox.core.lifecycle.IoExecutor; +import org.briarproject.nullsafety.NotNullByDefault; import java.util.List; +@NotNullByDefault public interface CircumventionProvider { enum BridgeType { DEFAULT_OBFS4, NON_DEFAULT_OBFS4, VANILLA, - MEEK + MEEK, + SNOWFLAKE } + /** + * Countries where Tor is blocked, i.e. vanilla Tor connection won't work. + * <p> + * See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 + * and https://trac.torproject.org/projects/tor/wiki/doc/OONI/censorshipwiki + */ + String[] BLOCKED = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"}; + /** * Countries where bridge connections are likely to work. - * Should be the union of + * Should be a subset of {@link #BLOCKED} and the union of * {@link #DEFAULT_BRIDGES}, {@link #NON_DEFAULT_BRIDGES} and * {@link #DPI_BRIDGES}. */ @@ -50,13 +61,14 @@ public interface CircumventionProvider { * Countries where non-default obfs4 or vanilla bridges are likely to work. * Should be a subset of {@link #BRIDGES}. */ - String[] NON_DEFAULT_BRIDGES = {"BY", "RU", "TM"}; + String[] NON_DEFAULT_BRIDGES = {"BY", "RU"}; /** * Countries where vanilla bridges are blocked via DPI but non-default - * obfs4 bridges and meek may work. Should be a subset of {@link #BRIDGES}. + * obfs4 bridges, meek and snowflake may work. Should be a subset of + * {@link #BRIDGES}. */ - String[] DPI_BRIDGES = {"CN", "IR"}; + String[] DPI_BRIDGES = {"CN", "IR", "TM"}; /** * Returns true if bridge connections of some type work in the given @@ -72,6 +84,6 @@ public interface CircumventionProvider { List<BridgeType> getSuitableBridgeTypes(String countryCode); @IoExecutor - List<String> getBridges(BridgeType type); - + List<String> getBridges(BridgeType type, String countryCode, + boolean letsEncrypt); } diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/CircumventionProviderImpl.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/CircumventionProviderImpl.java index b8c9c2e274b2d98d774ead55d0c4d269aeb4a983..2f5d3261f0ed36b918a7ace18b4ae1b1b607d372 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/CircumventionProviderImpl.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/CircumventionProviderImpl.java @@ -20,28 +20,35 @@ package org.briarproject.mailbox.core.tor; import org.briarproject.mailbox.core.lifecycle.IoExecutor; +import org.briarproject.nullsafety.NotNullByDefault; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Scanner; import java.util.Set; +import java.util.TreeMap; import javax.annotation.concurrent.Immutable; import javax.inject.Inject; import static java.util.Arrays.asList; -import static java.util.Objects.requireNonNull; import static org.briarproject.mailbox.core.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4; import static org.briarproject.mailbox.core.tor.CircumventionProvider.BridgeType.MEEK; import static org.briarproject.mailbox.core.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; +import static org.briarproject.mailbox.core.tor.CircumventionProvider.BridgeType.SNOWFLAKE; import static org.briarproject.mailbox.core.tor.CircumventionProvider.BridgeType.VANILLA; +import static org.briarproject.nullsafety.NullSafety.requireNonNull; @Immutable +@NotNullByDefault class CircumventionProviderImpl implements CircumventionProvider { private final static String BRIDGE_FILE_NAME = "bridges"; + private final static String SNOWFLAKE_PARAMS_FILE_NAME = "snowflake-params"; + private final static String DEFAULT_COUNTRY_CODE = "ZZ"; private static final Set<String> BRIDGE_COUNTRIES = new HashSet<>(asList(BRIDGES)); @@ -68,7 +75,7 @@ class CircumventionProviderImpl implements CircumventionProvider { } else if (NON_DEFAULT_BRIDGE_COUNTRIES.contains(countryCode)) { return asList(NON_DEFAULT_OBFS4, VANILLA); } else if (DPI_COUNTRIES.contains(countryCode)) { - return asList(NON_DEFAULT_OBFS4, MEEK); + return asList(NON_DEFAULT_OBFS4, MEEK, SNOWFLAKE); } else { return asList(DEFAULT_OBFS4, VANILLA); } @@ -76,7 +83,8 @@ class CircumventionProviderImpl implements CircumventionProvider { @Override @IoExecutor - public List<String> getBridges(BridgeType type) { + public List<String> getBridges(BridgeType type, String countryCode, + boolean letsEncrypt) { InputStream is = requireNonNull(getClass().getClassLoader() .getResourceAsStream(BRIDGE_FILE_NAME)); Scanner scanner = new Scanner(is); @@ -89,10 +97,45 @@ class CircumventionProviderImpl implements CircumventionProvider { (type == VANILLA && line.startsWith("v ")) || (type == MEEK && line.startsWith("m "))) { bridges.add(line.substring(2)); + } else if (type == SNOWFLAKE && line.startsWith("s ")) { + String params = getSnowflakeParams(countryCode, letsEncrypt); + bridges.add(line.substring(2) + " " + params); } } scanner.close(); return bridges; } + // Package access for testing + @SuppressWarnings("WeakerAccess") + String getSnowflakeParams(String countryCode, boolean letsEncrypt) { + Map<String, String> params = loadSnowflakeParams(); + if (countryCode.isEmpty()) countryCode = DEFAULT_COUNTRY_CODE; + // If we have parameters for this country code, return them + String value = params.get(makeKey(countryCode, letsEncrypt)); + if (value != null) return value; + // Return the default parameters + value = params.get(makeKey(DEFAULT_COUNTRY_CODE, letsEncrypt)); + return requireNonNull(value); + } + + private Map<String, String> loadSnowflakeParams() { + InputStream is = requireNonNull(getClass().getClassLoader() + .getResourceAsStream(SNOWFLAKE_PARAMS_FILE_NAME)); + Scanner scanner = new Scanner(is); + Map<String, String> params = new TreeMap<>(); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (line.length() < 5) continue; + String key = line.substring(0, 4); // Country code, space, digit + String value = line.substring(5); + params.put(key, value); + } + scanner.close(); + return params; + } + + private String makeKey(String countryCode, boolean letsEncrypt) { + return countryCode + " " + (letsEncrypt ? "1" : "0"); + } } diff --git a/mailbox-core/src/main/resources/bridges b/mailbox-core/src/main/resources/bridges index f45a54720fc6383939f42515988030fb098d92fa..e365f524259407c5552a84daac9012cfd35a9590 100644 --- a/mailbox-core/src/main/resources/bridges +++ b/mailbox-core/src/main/resources/bridges @@ -1,6 +1,4 @@ d Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1 -d Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1 -d Bridge obfs4 38.229.33.83:80 0BAC39417268B96B9F514E7F63FA6FBA1A788955 cert=VwEFpk9F/UN9JED7XpG1XOjm/O8ZCXK80oPecgWnNDZDv5pdkhq1OpbAH0wNqOT6H6BmRQ 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 @@ -11,23 +9,27 @@ d Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=s 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 -n Bridge obfs4 46.226.107.197:10300 A38FD6BDFD902882F5F5B9B7CCC95602A20B0BC4 cert=t8tA9q2AeGlmp/dO6oW9bkY5RqqmvqjArCEM9wjJoDnk6XtnaejkF0JTA7VamdyOzcvuBg 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 74.104.165.202:9002 EF432018A6AA5D970B2F84E39CD30A147030141C cert=PhppfUusY85dHGvWtGTybZ1fED4DtbHmALkNMIOIYrAz1B4xN7/2a5gyiZe1epju1BOHVg 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 -n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert=+q0pjaiM0JMqHL/BKqCRD+pjflaw/S406eUDF7CnFgamvQW3l2HVLJhQ6uX9P8zff0PLGg iat-mode=0 -n Bridge obfs4 65.108.159.114:14174 E1AD374BA9F34BD98862D128AC54D40C7DC138AE cert=YMkxMSBN2OOd99AkJpFaEUyKkdqZgJt9oVJEgO1QnT37n/Vc2yR4nhx4k4VkPLfEP1f4eg iat-mode=0 n Bridge obfs4 185.177.207.138:8443 53716FE26F23C8C6A71A2BC5D9D8DC64747278C7 cert=6jcYVilMEzxdsWghSrQFpYYJlkkir/GPIXw/EnddUK3S8ToVpMG8u1SwMOEdEs735RrMHw iat-mode=0 -n Bridge obfs4 176.123.2.253:1933 B855D141CE6C4DE0F7EA4AAED83EBA8373FA8191 cert=1rOoSaRagc6PPR//paIl+ukv1N+xWKCdBXMFxK0k/moEwH0lk5bURBrUDzIX35fVzaiicQ iat-mode=0 -n Bridge obfs4 5.252.176.61:9418 3E61130100AD827AB9CB33DAC052D9BC49A39509 cert=/aMyBPnKbQFISithD3i1KHUdpWeMpWG3SvUpq1YWCf2EQohFxQfw+646gW1knm4BI/DLRA iat-mode=0 -n Bridge obfs4 202.61.224.111:6902 A4F91299763DB925AE3BD29A0FC1A9821E5D9BAE cert=NBKm2MJ83wMvYShkqpD5RrbDtW5YpIZrFNnMw7Dj1XOM3plU60Bh4eziaQXe8fGtb8ZqKg iat-mode=0 -n Bridge obfs4 87.121.72.109:9002 C8081D4731C953FA4AE166946E72B29153351E34 cert=bikAqxKV6Ch5gFCBTdPI28VeShYa1ZgkLmvc7YZNLWFsFZoaCULL/3AQKjpIfvSiJs5jGQ iat-mode=0 -n Bridge obfs4 172.104.17.96:17900 B6B37AC96E163D0A5ECE55826D17B50B70F0A7F8 cert=gUz7svhPxoALgeN4lMYrXK7NBnaDqwu6SKRJOhaO9IIMBpnB8UhMCMKzzMho3b0RxWzBVg iat-mode=0 -n Bridge obfs4 70.34.249.113:443 F441B16ABB1055794C2CE01821FC05047B2C8CFC cert=MauLNoyq8EwjY4Qe0oASYzs2hXdSjNgy+BtP9oo1naHhRsyKTtAZzeNv08RnzWjMJrTwcg iat-mode=0 -v Bridge 135.181.113.164:54444 74AF4CCA614C454B7D3E81FF8BACD78CEBC7D7DE +n Bridge obfs4 45.142.181.131:42069 6EBCF6B02DA2B982F4080A7119D737366AFB74FA cert=9HyWH/BCwWzNirZdTQtluCgJk+gFhpOqydIuyQ1iDvpnqsAynKF+zcPE/INZFefm86UlBg iat-mode=0 +n Bridge obfs4 152.67.77.101:4096 B82DB9CDDF887AB8A859420E07DF298E30AF8A6E cert=21OWn3yFo+hulmQNAOtF5uwwOqWtdT5PrLhk8BG9DpOd0/k5DEkQEYPyDdXbS9nZ0E5BJA iat-mode=0 +n Bridge obfs4 94.142.246.132:8088 135C158527AA9FE9A2F26EC515EB6999D813D347 cert=wTUz0/5FhAZRkitil5MprGbSF3JzjxjxI1kAmxAdSeDy98NgcLr11f/qUXWDC76Y97RiSg iat-mode=0 +n Bridge obfs4 152.70.180.20:1993 3327C43587E66AD5F874C4234A1D72C938AD7318 cert=s7xLRUO2psaX7TMUP2YhXdxItR4U6K7D+E3gQaS/+yWUppevtazIibq4dN1g5Reu6dD2QQ iat-mode=0 +n Bridge obfs4 144.202.12.254:10002 4E220F45CD404C8A3082A36326A5ED19BB8D4404 cert=iLz5YYWO4pUw7U7MRNOSvE0qO+IVeE4kVfFVWPO3coH3FmZtrkvlaTklfXxHZaCcXWBgaA iat-mode=0 +n Bridge obfs4 109.14.168.159:5082 BFE1416DEFFE969581F016A4A319A87FFB26BA91 cert=n3X1CDdKBPXPIzfKh83p3ydfMzb0AD9gKC+/gIpHb7+xjjAnYO9x3LT+T/MvOIfAXxYySg iat-mode=0 +n Bridge obfs4 185.177.207.132:8443 4FB781F7A9DD39DA53A7996907817FC479874D19 cert=UL2gCAXWW5kEWY4TQ0lNeu6OAmzh40bXYVhMnTWVG8USnyy/zEKGSIPgmwTDMumWr9c1Pg iat-mode=0 +n Bridge obfs4 213.108.110.149:7499 519344140473CF91030B08F91521F9A6C144ED6C cert=k9fSL/d491qAkGmi2VeSwVlfuyO02jBeN54qxzzQISxpfm3b+a6kJpo8/Bfy1ACbHZIJUg iat-mode=0 +n Bridge obfs4 158.174.114.97:3456 32665CD4CBE19092CA47A53D317B8BFF5810441C cert=ne5Zt0TcMedSGmFwAs/AV6J6E9Hn7mG5mR6vQNpEfyuCZK1VRpQvU1LvvtesSu4CXqZtYQ iat-mode=0 +n Bridge obfs4 64.4.175.62:8000 8B72740D150795ACB5101AA5F95D1ACDA4FE6B3E cert=vduuNhJ5U/8hjZmllP6AFfXSlSZsnrimdR8Tm8DY9dxWS4n2j92fNc0qHihUwRqwcOfIcg iat-mode=0 +n Bridge obfs4 82.64.115.17:990 B08238781C2CD80DBD95AEABEB6F6C75F2E2CEB6 cert=1udeMlFNs3sJ20zwpPE6nShZqqwDb3F1ET4KzfSfD+fktkue9zNx9H3t+yLCPAsg+6UTUA iat-mode=1 +n Bridge obfs4 87.161.120.147:9292 9418EEBE8AEAE32CC381AF51610366E8B24651E0 cert=DFRm74qsD1i2/ypaGochpX6CS1j9JTFAKEYaHXrgrx6M2LG5Cvppdt3Ob7lULfhqgtAUdg iat-mode=0 +n Bridge obfs4 157.90.245.231:8599 C23CD468EC04555E2B37BE81A771E681049DEA6A cert=UsmDelrbwg4jc6BMvZJ0TS8klUIa2qkbRu3xwQc3ZXPEgpMqyTYUxcVwyPbIU5KmAHsmAA iat-mode=0 v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F -v Bridge 77.96.91.103:443 ED000A75B79A58F1D83A4D1675C2A9395B71BE8E v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31 -m Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com +v Bridge 213.196.191.96:9060 05E222E2A8C234234FE0CEB58B08A93B8FC360DB +v Bridge 75.100.92.154:22815 465E990FA8A752DDE861EDF31E42454ACC037AB4 +v Bridge 87.100.193.2:9010 13FB63452AADFA082BD2BC3E1E320AD301F07877 +v Bridge 65.21.240.163:33245 20BD59649212CFE7412BFC9B94C3CCCFD8F807A8 +m Bridge meek_lite 192.0.2.18:80 BE776A53492E1E044A26F17306E1BC46A55A1625 url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com utls=hellochrome_auto +s Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 diff --git a/mailbox-core/src/main/resources/snowflake-params b/mailbox-core/src/main/resources/snowflake-params new file mode 100644 index 0000000000000000000000000000000000000000..a6cbd85636605b68128edef8e9628896c25b879f --- /dev/null +++ b/mailbox-core/src/main/resources/snowflake-params @@ -0,0 +1,4 @@ +ZZ 1 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellochrome_auto +ZZ 0 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellochrome_auto +TM 1 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479 utls-imitate=hellochrome_auto +TM 0 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479 utls-imitate=hellochrome_auto