Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions app/src/main/java/org/fptn/vpn/enums/BypassCensorshipMethod.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
package org.fptn.vpn.enums;

public enum BypassCensorshipMethod {
SNI_SPOOFING, // deprecated
TLS_OBFUSCATION,
SNI_REALITY, // deprecated
/* Chrome */
SNI_REALITY_CHROME_147,
SNI_REALITY_CHROME_146,
SNI_REALITY_CHROME_145,
/* Firefox */
SNI_REALITY_FIREFOX_149,
/* Yandex Browser */
SNI_REALITY_YANDEX_26,
SNI_REALITY_YANDEX_25,
SNI_REALITY_YANDEX_24,
/* Safari */
SNI_REALITY_SAFARI_26
SNI_REALITY
}
16 changes: 16 additions & 0 deletions app/src/main/java/org/fptn/vpn/enums/SniSpoofingMode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.fptn.vpn.enums;

public enum SniSpoofingMode {
/* Chrome */
SNI_REALITY_CHROME_147,
SNI_REALITY_CHROME_146,
SNI_REALITY_CHROME_145,
/* Firefox */
SNI_REALITY_FIREFOX_149,
/* Yandex Browser */
SNI_REALITY_YANDEX_26,
SNI_REALITY_YANDEX_25,
SNI_REALITY_YANDEX_24,
/* Safari */
SNI_REALITY_SAFARI_26
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package org.fptn.vpn.services.speedtest;

import android.util.Log;

import org.fptn.vpn.database.entity.ServerEntity;
import org.fptn.vpn.enums.BypassCensorshipMethod;
import org.fptn.vpn.enums.SniSpoofingMode;
import org.fptn.vpn.services.websocket.NativeHttpsClientImpl;
import org.fptn.vpn.services.websocket.NativeResponse;
import org.fptn.vpn.vpnclient.exception.PVNClientException;
Expand All @@ -22,14 +21,15 @@ public class NativeSpeedTestTask implements Callable<NativeSpeedTestResult> {
private final ServerEntity serverEntity;
private final NativeHttpsClientImpl nativeHttpsClient;

public NativeSpeedTestTask(ServerEntity serverEntity, String sniHost, BypassCensorshipMethod censorshipStrategy) {
public NativeSpeedTestTask(ServerEntity serverEntity, String sniHost, BypassCensorshipMethod censorshipStrategy, SniSpoofingMode sniSpoofingMode) {
this.serverEntity = serverEntity;
this.nativeHttpsClient = new NativeHttpsClientImpl(
serverEntity.getHost(),
serverEntity.getPort(),
serverEntity.getMd5ServerFingerprint(),
sniHost,
censorshipStrategy
censorshipStrategy,
sniSpoofingMode
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import org.fptn.vpn.database.entity.ServerEntity;
import org.fptn.vpn.enums.BypassCensorshipMethod;
import org.fptn.vpn.enums.SniSpoofingMode;
import org.fptn.vpn.vpnclient.exception.ErrorCode;
import org.fptn.vpn.vpnclient.exception.PVNClientException;

Expand All @@ -23,7 +24,7 @@ public class SpeedTestUtils {
private static final long SEARCH_BEST_SERVER_MAX_TIMEOUT = 30L;
private static final String PREMIUM_KEYWORD = "premium";

public static ServerEntity findFastestServer(List<ServerEntity> serverEntityList, String sniHostName, BypassCensorshipMethod censorshipStrategy) throws PVNClientException {
public static ServerEntity findFastestServer(List<ServerEntity> serverEntityList, String sniHostName, BypassCensorshipMethod censorshipStrategy, SniSpoofingMode sniSpoofingMode) throws PVNClientException {
Log.d(TAG, "SpeedTestUtils.findFastestServer() start: " + Instant.now() + ", Thread.Id: " + Thread.currentThread().getId());

if (serverEntityList != null && !serverEntityList.isEmpty()) {
Expand All @@ -33,7 +34,7 @@ public static ServerEntity findFastestServer(List<ServerEntity> serverEntityList

ExecutorService executor = Executors.newFixedThreadPool(selectedServers.size());
List<NativeSpeedTestTask> nativeSpeedTestTaskList = selectedServers.stream()
.map(fptnServerDto -> new NativeSpeedTestTask(fptnServerDto, sniHostName, censorshipStrategy))
.map(fptnServerDto -> new NativeSpeedTestTask(fptnServerDto, sniHostName, censorshipStrategy, sniSpoofingMode))
.collect(Collectors.toList());
try {
NativeSpeedTestResult bestResult = executor.invokeAny(nativeSpeedTestTaskList, SEARCH_BEST_SERVER_MAX_TIMEOUT, TimeUnit.SECONDS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.fptn.vpn.enums.ConnectionState;
import org.fptn.vpn.enums.NetworkType;
import org.fptn.vpn.enums.PerAppVpnMode;
import org.fptn.vpn.enums.SniSpoofingMode;
import org.fptn.vpn.services.websocket.DnsServers;
import org.fptn.vpn.services.websocket.WebSocketAlreadyShutdownException;
import org.fptn.vpn.services.websocket.WebSocketClientWrapper;
Expand Down Expand Up @@ -80,6 +81,7 @@ public class FptnConnection extends Thread {

@Getter
private final AtomicInteger reconnectCount = new AtomicInteger(0);
private final SniSpoofingMode sniSpoofingMode;

@Setter
private PendingIntent configureVpnIntent;
Expand Down Expand Up @@ -114,6 +116,7 @@ public FptnConnection(final FptnService service,
final int delayBetweenAttempts,
final String sniHostName,
final BypassCensorshipMethod censorshipStrategy,
final SniSpoofingMode sniSpoofingMode,
final PerAppVpnMode perAppVpnMode,
final List<AppInfo> appInfos) throws UnknownHostException {
this.service = service;
Expand All @@ -123,6 +126,7 @@ public FptnConnection(final FptnService service,
this.currentNetworkType = currentNetworkType;
this.sniHostName = sniHostName;
this.censorshipStrategy = censorshipStrategy;
this.sniSpoofingMode = sniSpoofingMode;
this.perAppVpnMode = perAppVpnMode;
this.appInfos = appInfos;

Expand All @@ -135,7 +139,8 @@ public FptnConnection(final FptnService service,
this::onMessageReceived,
this::onConnectionFailure,
this.sniHostName,
this.censorshipStrategy
this.censorshipStrategy,
this.sniSpoofingMode
);

this.maxReconnectCount = maxReconnectCount;
Expand Down
14 changes: 13 additions & 1 deletion app/src/main/java/org/fptn/vpn/services/vpn/FptnService.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.fptn.vpn.enums.ConnectionState;
import org.fptn.vpn.enums.NetworkType;
import org.fptn.vpn.enums.PerAppVpnMode;
import org.fptn.vpn.enums.SniSpoofingMode;
import org.fptn.vpn.services.tile.FptnTileService;
import org.fptn.vpn.utils.NetworkUtils;
import org.fptn.vpn.utils.NotificationUtils;
Expand Down Expand Up @@ -243,6 +244,11 @@ public int onStartCommand(Intent intent, int flags, int startId) {
String sniHostname = SharedPrefUtils.getSniHostname(getApplicationContext());
BypassCensorshipMethod bypassCensorshipMethod = SharedPrefUtils.getBypassCensorshipMethod(this);

SniSpoofingMode sniSpoofingMode = null;
if (bypassCensorshipMethod == BypassCensorshipMethod.SNI_REALITY){
sniSpoofingMode = SharedPrefUtils.getSniSpoofingMode(this);
}

int serverId = intent.getIntExtra(SELECTED_SERVER, SELECTED_SERVER_ID_AUTO);

// Process startService from TileService
Expand Down Expand Up @@ -272,7 +278,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
updateNotificationWithMessage(getString(R.string.connecting_auto), "");

List<ServerEntity> serverEntities = appDatabase.serverDAO().getServerList(false);
ServerEntity server = SpeedTestUtils.findFastestServer(serverEntities, sniHostname, bypassCensorshipMethod);
ServerEntity server = SpeedTestUtils.findFastestServer(serverEntities, sniHostname, bypassCensorshipMethod, sniSpoofingMode);
setSelectedServer(server.getId());

connect(server, sniHostname);
Expand Down Expand Up @@ -451,6 +457,11 @@ private void connect(ServerEntity serverEntity, String sniHostname) throws Unkno

BypassCensorshipMethod bypassCensorshipMethod = SharedPrefUtils.getBypassCensorshipMethod(this);

SniSpoofingMode sniSpoofingMode = null;
if (bypassCensorshipMethod == BypassCensorshipMethod.SNI_REALITY){
sniSpoofingMode = SharedPrefUtils.getSniSpoofingMode(this);
}

PerAppVpnMode perAppVpnMode = SharedPrefUtils.getPerAppVPNMode(this);
List<AppInfo> appInfos = new ArrayList<>();
if (perAppVpnMode == PerAppVpnMode.ONLY_ALLOWED || perAppVpnMode == PerAppVpnMode.EXCEPT_DISALLOWED) {
Expand Down Expand Up @@ -478,6 +489,7 @@ private void connect(ServerEntity serverEntity, String sniHostname) throws Unkno
delayBetweenAttempts,
sniHostname,
bypassCensorshipMethod,
sniSpoofingMode,
perAppVpnMode,
appInfos
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.util.Log;

import org.fptn.vpn.enums.BypassCensorshipMethod;
import org.fptn.vpn.enums.SniSpoofingMode;

public class NativeHttpsClientImpl {
private static final String TAG = NativeHttpsClientImpl.class.getName();
Expand All @@ -17,37 +18,15 @@ public NativeHttpsClientImpl(String serverIP,
int serverPort,
String md5Fingerprint,
String sni,
BypassCensorshipMethod censorshipStrategy) {
BypassCensorshipMethod censorshipStrategy,
SniSpoofingMode sniSpoofingMode) {
String censorshipStrategyName = "SNI-REALITY-YANDEX-25";
if (censorshipStrategy == BypassCensorshipMethod.TLS_OBFUSCATION) {
censorshipStrategyName = "OBFUSCATION";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY) { // deprecated
censorshipStrategyName = "SNI-REALITY";
}
/* Chrome */
else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_CHROME_147) {
censorshipStrategyName = "SNI-REALITY-CHROME-147";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_CHROME_146) {
censorshipStrategyName = "SNI-REALITY-CHROME-146";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_CHROME_145) {
censorshipStrategyName = "SNI-REALITY-CHROME-145";
}
/* Firefox */
else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_FIREFOX_149) {
censorshipStrategyName = "SNI-REALITY-FIREFOX-149";
}
/* Yandex */
else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_YANDEX_26) {
censorshipStrategyName = "SNI-REALITY-YANDEX-26";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_YANDEX_25) {
censorshipStrategyName = "SNI-REALITY-YANDEX-25";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_YANDEX_24) {
censorshipStrategyName = "SNI-REALITY-YANDEX-24";
}
/* Safari */
else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_SAFARI_26) {
censorshipStrategyName = "SNI-REALITY-SAFARI-26";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY) {
censorshipStrategyName = sniSpoofingMode.toString().replace('_', '-');
}

this.nativeHandle = nativeCreate(
serverIP,
serverPort,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.util.Log;

import org.fptn.vpn.enums.BypassCensorshipMethod;
import org.fptn.vpn.enums.SniSpoofingMode;
import org.fptn.vpn.services.websocket.callback.OnFailureCallback;
import org.fptn.vpn.services.websocket.callback.OnMessageReceivedCallback;
import org.fptn.vpn.services.websocket.callback.OnOpenCallback;
Expand Down Expand Up @@ -39,40 +40,17 @@ public NativeWebSocketClientImpl(
OnMessageReceivedCallback onMessageReceivedCallback,
OnFailureCallback onFailureCallback,
String sniHostName,
BypassCensorshipMethod censorshipStrategy) throws PVNClientException {
BypassCensorshipMethod censorshipStrategy,
SniSpoofingMode sniSpoofingMode) throws PVNClientException {
this.onOpenCallback = onOpenCallback;
this.onMessageReceivedCallback = onMessageReceivedCallback;
this.onFailureCallback = onFailureCallback;

String censorshipStrategyName = "SNI-REALITY-YANDEX-25";
if (censorshipStrategy == BypassCensorshipMethod.TLS_OBFUSCATION) {
censorshipStrategyName = "OBFUSCATION";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY) { // deprecated
censorshipStrategyName = "SNI-REALITY";
}
/* Chrome */
else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_CHROME_147) {
censorshipStrategyName = "SNI-REALITY-CHROME-147";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_CHROME_146) {
censorshipStrategyName = "SNI-REALITY-CHROME-146";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_CHROME_145) {
censorshipStrategyName = "SNI-REALITY-CHROME-145";
}
/* Firefox */
else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_FIREFOX_149) {
censorshipStrategyName = "SNI-REALITY-FIREFOX-149";
}
/* Yandex */
else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_YANDEX_26) {
censorshipStrategyName = "SNI-REALITY-YANDEX-26";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_YANDEX_25) {
censorshipStrategyName = "SNI-REALITY-YANDEX-25";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_YANDEX_24) {
censorshipStrategyName = "SNI-REALITY-YANDEX-24";
}
/* Safari */
else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY_SAFARI_26) {
censorshipStrategyName = "SNI-REALITY-SAFARI-26";
} else if (censorshipStrategy == BypassCensorshipMethod.SNI_REALITY) {
censorshipStrategyName = sniSpoofingMode.toString().replace('_', '-');
}

this.nativeHandle = nativeCreate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import org.fptn.vpn.database.entity.ServerEntity;
import org.fptn.vpn.enums.BypassCensorshipMethod;
import org.fptn.vpn.enums.SniSpoofingMode;
import org.fptn.vpn.services.websocket.callback.OnFailureCallback;
import org.fptn.vpn.services.websocket.callback.OnMessageReceivedCallback;
import org.fptn.vpn.services.websocket.callback.OnOpenCallback;
Expand All @@ -29,6 +30,7 @@ public class WebSocketClientWrapper {
private final NativeHttpsClientImpl nativeHttpsClient;
private final String sniHostName;
private final BypassCensorshipMethod censorshipStrategy;
private final SniSpoofingMode sniSpoofingMode;

private NativeWebSocketClientImpl nativeWebSocketClient;

Expand All @@ -42,7 +44,8 @@ public WebSocketClientWrapper(ServerEntity serverEntity,
OnMessageReceivedCallback onMessageReceivedCallback,
OnFailureCallback onFailureCallback,
String sniHostName,
BypassCensorshipMethod censorshipStrategy) {
BypassCensorshipMethod censorshipStrategy,
SniSpoofingMode sniSpoofingMode) {
this.serverEntity = serverEntity;
this.tunAddressIPv4 = tunAddressIPv4;
this.tunAddressIPv6 = tunAddressIPv6;
Expand All @@ -53,13 +56,15 @@ public WebSocketClientWrapper(ServerEntity serverEntity,
// this is SNI spoofing
this.sniHostName = sniHostName;
this.censorshipStrategy = censorshipStrategy;
this.sniSpoofingMode = sniSpoofingMode;

this.nativeHttpsClient = new NativeHttpsClientImpl(
serverEntity.getHost(),
serverEntity.getPort(),
serverEntity.getMd5ServerFingerprint(),
sniHostName,
censorshipStrategy
censorshipStrategy,
sniSpoofingMode
);
}

Expand All @@ -82,7 +87,8 @@ public synchronized void startWebSocket() throws PVNClientException, WebSocketAl
onMessageReceivedCallback,
onFailureCallback,
sniHostName,
censorshipStrategy
censorshipStrategy,
sniSpoofingMode
);

Log.d(getTag(), "startWebSocket() nativeWebSocketClient.start() Thread.id: " + Thread.currentThread().getId());
Expand Down
20 changes: 19 additions & 1 deletion app/src/main/java/org/fptn/vpn/utils/SharedPrefUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.fptn.vpn.core.common.Constants;
import org.fptn.vpn.enums.BypassCensorshipMethod;
import org.fptn.vpn.enums.PerAppVpnMode;
import org.fptn.vpn.enums.SniSpoofingMode;

import java.util.Objects;

Expand Down Expand Up @@ -127,14 +128,31 @@ public static BypassCensorshipMethod getBypassCensorshipMethod(Context context)
return value;
}
}
return BypassCensorshipMethod.SNI_SPOOFING;
return BypassCensorshipMethod.SNI_REALITY;
}

public static void saveBypassCensorshipMethod(Context context, BypassCensorshipMethod method) {
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.APPLICATION_SHARED_PREFERENCES, Context.MODE_PRIVATE);
sharedPreferences.edit().putString(Constants.BYPASS_CENSORSHIP_METHOD_SHARED_PREF_KEY, method.toString()).apply();
}


public static SniSpoofingMode getSniSpoofingMode(Context context) {
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.APPLICATION_SHARED_PREFERENCES, Context.MODE_PRIVATE);
String modeName = sharedPreferences.getString(Constants.SNI_SPOOFING_MODE_SHARED_PREF_KEY, null);
for (SniSpoofingMode value : SniSpoofingMode.values()) {
if (Objects.equals(modeName, value.name())) {
return value;
}
}
return SniSpoofingMode.SNI_REALITY_YANDEX_25;
}

public static void saveSniSpoofingMode(Context context, SniSpoofingMode mode) {
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.APPLICATION_SHARED_PREFERENCES, Context.MODE_PRIVATE);
sharedPreferences.edit().putString(Constants.SNI_SPOOFING_MODE_SHARED_PREF_KEY, mode.toString()).apply();
}

/* Per-app VPN settings */
public static PerAppVpnMode getPerAppVPNMode(Context context) {
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.APPLICATION_SHARED_PREFERENCES, Context.MODE_PRIVATE);
Expand Down
Loading