Skip to content

Commit

Permalink
Improve country detection
Browse files Browse the repository at this point in the history
  • Loading branch information
trudyhood committed Dec 9, 2024
1 parent d9df3bf commit b34796d
Show file tree
Hide file tree
Showing 14 changed files with 110 additions and 75 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* Update: Add token tags
* Update: Add server tags
* Update: Generate anonymous client ID from device ID
* Update: Detect client country for exclude my country
* Fix: Startup crash on some Android devices
* Fix: IPv6 Ping

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Android.Content.PM;
using Android.Content.Res;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Webkit;
Expand All @@ -15,6 +17,7 @@ public class AndroidAppWebViewMainActivityHandler(
{
private bool _isWeViewVisible;
private WebView? WebView { get; set; }
public Exception? WebViewCreateException { get; private set; }

protected override void OnCreate(Bundle? savedInstanceState)
{
Expand Down Expand Up @@ -105,25 +108,48 @@ private string GetLaunchUrl()

private void InitWebUi()
{
WebView = new WebView(ActivityEvent.Activity);
WebView.Settings.JavaScriptEnabled = true;
WebView.Settings.DomStorageEnabled = true;
WebView.Settings.JavaScriptCanOpenWindowsAutomatically = true;
WebView.Settings.SetSupportMultipleWindows(true);
WebView.SetLayerType(LayerType.Hardware, null);
if (VpnHoodApp.Instance.Resource.Colors.WindowBackgroundColor != null)
WebView.SetBackgroundColor(VpnHoodApp.Instance.Resource.Colors.WindowBackgroundColor.Value
.ToAndroidColor());

var webViewClient = new AndroidAppWebViewClient();
webViewClient.PageLoaded += WebViewClient_PageLoaded;
WebView.SetWebViewClient(webViewClient);
WebView.SetWebChromeClient(new AndroidAppWebChromeClient());
try {
WebView = new WebView(ActivityEvent.Activity);
WebView.Settings.JavaScriptEnabled = true;
WebView.Settings.DomStorageEnabled = true;
WebView.Settings.JavaScriptCanOpenWindowsAutomatically = true;
WebView.Settings.SetSupportMultipleWindows(true);
WebView.SetLayerType(LayerType.Hardware, null);
if (VpnHoodApp.Instance.Resource.Colors.WindowBackgroundColor != null)
WebView.SetBackgroundColor(VpnHoodApp.Instance.Resource.Colors.WindowBackgroundColor.Value
.ToAndroidColor());

var webViewClient = new AndroidAppWebViewClient();
webViewClient.PageLoaded += WebViewClient_PageLoaded;
WebView.SetWebViewClient(webViewClient);
WebView.SetWebChromeClient(new AndroidAppWebChromeClient());

#if DEBUG
WebView.SetWebContentsDebuggingEnabled(true);
WebView.SetWebContentsDebuggingEnabled(true);
#endif
WebView.LoadUrl(GetLaunchUrl());
WebView.LoadUrl(GetLaunchUrl());
}
catch (Exception ex) {
WebViewCreateException = ex;
InitWebViewExceptionMessage(ex);
}
}

private void InitWebViewExceptionMessage(Exception ex)
{
// get all current cpu architecture in a string
var cpuArch = string.Join(", ", Build.SupportedAbis ?? []);

// Create a new TextView and set it as the content of the activity
var textView = new TextView(ActivityEvent.Activity);
textView.Text = $"WebView initialization failed. Please update your Android System WebView and Chrome Browser. " +
$"CpuArchitectures: {cpuArch}, Error: {ex.Message}";
textView.TextSize = 20;

// set font color to red
textView.SetTextColor(Color.Red);
ActivityEvent.Activity.SetContentView(textView);

}

private void WebViewClient_PageLoaded(object? sender, EventArgs e)
Expand Down
14 changes: 12 additions & 2 deletions VpnHood.Client.App/AppPersistState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,19 @@ public string? ClientCountryCode {
}
}

[JsonIgnore]
public string? ClientCountryName => VhUtil.TryGetCountryName(ClientCountryCode);
// prop
private string? _clientCountryCodeByServer;
public string? ClientCountryCodeByServer {
get => _clientCountryCodeByServer;
set {
if (_clientCountryCodeByServer == value)
return;

// set country code and its name
_clientCountryCodeByServer = value?.ToUpper();
Save();
}
}

internal static AppPersistState Load(string filePath)
{
Expand Down
9 changes: 5 additions & 4 deletions VpnHood.Client.App/ClientProfiles/ClientProfileService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,24 +191,25 @@ internal ClientProfile[] ImportBuiltInAccessKeys(string[] accessKeys)
return clientProfiles.ToArray();
}

public Token UpdateTokenByAccessKey(Token token, string accessKey)
public bool UpdateTokenByAccessKey(string tokenId, string accessKey)
{
try {
var token = GetToken(tokenId);
var newToken = Token.FromAccessKey(accessKey);
if (VhUtil.JsonEquals(token, newToken))
return token;
return false;

if (token.TokenId != newToken.TokenId)
throw new Exception("Could not update the token via access key because its token ID is not the same.");

// allow to overwrite builtIn because update token is from internal source and can update itself
ImportAccessToken(newToken, overwriteNewer: true, allowOverwriteBuiltIn: true);
VhLogger.Instance.LogInformation("ServerToken has been updated.");
return newToken;
return true;
}
catch (Exception ex) {
VhLogger.Instance.LogError(ex, "Could not update token from the given access-key.");
return token;
return false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class ClientServerLocationInfo : ServerLocationInfo

public static ClientServerLocationInfo[] CreateFromToken(Token token)
{
var clientCountry = VpnHoodApp.Instance.GetCurrentCountryCode();
var clientCountry = VpnHoodApp.Instance.GetClientCountryByServer();

// get country policy
var policy = token.ClientPolicies?.FirstOrDefault(x => x.ClientCountries.Any(y => y.Equals(clientCountry, StringComparison.OrdinalIgnoreCase))) ??
Expand Down
Binary file modified VpnHood.Client.App/Resources/IpLocations.zip
Binary file not shown.
4 changes: 2 additions & 2 deletions VpnHood.Client.App/Services/AppAdService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class AppAdService(

public async Task LoadAd(IUiContext uiContext, CancellationToken cancellationToken)
{
var countryCode = await regionProvider.GetCurrentCountryCodeAsync(cancellationToken);
var countryCode = await regionProvider.GetCurrentCountryAsync(cancellationToken);
await _compositeInterstitialAdService.LoadAd(uiContext, countryCode: countryCode, forceReload: false,
cancellationToken);
}
Expand All @@ -47,7 +47,7 @@ private async Task<ShowedAdResult> ShowAd(AppCompositeAdService appCompositeAdSe
IUiContext uiContext, string sessionId, CancellationToken cancellationToken)
{
var adData = $"sid:{sessionId};ad:{Guid.NewGuid()}";
var countryCode = await regionProvider.GetCurrentCountryCodeAsync(cancellationToken);
var countryCode = await regionProvider.GetCurrentCountryAsync(cancellationToken);
await appCompositeAdService.LoadAd(uiContext, countryCode: countryCode, forceReload: false, cancellationToken);
var networkName = await appCompositeAdService.ShowLoadedAd(uiContext, adData, cancellationToken);
var showAdResult = new ShowedAdResult {
Expand Down
51 changes: 28 additions & 23 deletions VpnHood.Client.App/VpnHoodApp.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System.Globalization;
using System.IO.Compression;
using System.Net;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Ga4.Trackers;
using Ga4.Trackers.Ga4Tags;
using Microsoft.Extensions.Logging;
using VpnHood.Client.Abstractions;
using VpnHood.Client.App.ClientProfiles;
using VpnHood.Client.App.Providers;
using VpnHood.Client.App.Services;
Expand All @@ -33,7 +31,7 @@
namespace VpnHood.Client.App;

public class VpnHoodApp : Singleton<VpnHoodApp>,
IAsyncDisposable, IIpRangeProvider, IJob, IRegionProvider
IAsyncDisposable, IJob, IRegionProvider
{
private const string FileNameLog = "log.txt";
private const string FileNameSettings = "settings.json";
Expand Down Expand Up @@ -305,7 +303,7 @@ is AppConnectionState.Connected or AppConnectionState.Connecting
TcpTunnelledCount = client?.Stat.TcpTunnelledCount,
TcpPassthruCount = client?.Stat.TcpPassthruCount,
ClientCountryCode = _appPersistState.ClientCountryCode,
ClientCountryName = _appPersistState.ClientCountryName,
ClientCountryName = VhUtil.TryGetCountryName(_appPersistState.ClientCountryCode),
IsWaitingForAd = client?.Stat.IsWaitingForAd is true,
ConnectRequestTime = _connectRequestTime,
IsUdpChannelSupported = client?.Stat.IsUdpChannelSupported,
Expand Down Expand Up @@ -455,7 +453,7 @@ public async Task Connect(
JsonSerializer.Serialize(UserSettings, new JsonSerializerOptions { WriteIndented = true }));
if (diagnose) // log country name
VhLogger.Instance.LogInformation("CountryCode: {CountryCode}",
VhUtil.TryGetCountryName(await GetCurrentCountryCodeAsync(cancellationToken).VhConfigureAwait()));
VhUtil.TryGetCountryName(await GetCurrentCountryAsync(cancellationToken).VhConfigureAwait()));

VhLogger.Instance.LogInformation("VpnHood Client is Connecting ...");

Expand Down Expand Up @@ -554,7 +552,7 @@ private async Task ConnectInternal(Token token, string? serverLocation, string?
ReconnectTimeout = _reconnectTimeout,
AutoWaitTimeout = _autoWaitTimeout,
IncludeLocalNetwork = UserSettings.IncludeLocalNetwork,
IpRangeProvider = this,
IncludeIpRanges = await GetIncludeIpRanges(cancellationToken),
AdService = Services.AdService,
PacketCaptureIncludeIpRanges = packetCaptureIpRanges,
MaxDatagramChannelCount = UserSettings.MaxDatagramChannelCount,
Expand Down Expand Up @@ -591,7 +589,8 @@ private async Task ConnectInternal(Token token, string? serverLocation, string?
client.StateChanged += Client_StateChanged;
_client = client;

VhLogger.Instance.LogTrace("Engine is connecting... HasDiagnoseStarted: {HasDiagnoseStarted}, AutoDiagnose: {AutoDiagnose}",
VhLogger.Instance.LogTrace(
"Engine is connecting... HasDiagnoseStarted: {HasDiagnoseStarted}, AutoDiagnose: {AutoDiagnose}",
_hasDiagnoseStarted, _autoDiagnose);

if (_hasDiagnoseStarted)
Expand All @@ -603,18 +602,13 @@ private async Task ConnectInternal(Token token, string? serverLocation, string?

// set connected time
ConnectedTime = DateTime.Now;

// update access token if ResponseAccessKey is set
if (client.ResponseAccessKey != null)
_ = ClientProfileService.UpdateTokenByAccessKey(token, client.ResponseAccessKey);
UpdateStatusByCreatedClient(client);

// check version after first connection
_ = VersionCheck();
}
catch (Exception) when (client is not null) {
// update access token if ResponseAccessKey is set
if (!string.IsNullOrWhiteSpace(client.ResponseAccessKey))
_ = ClientProfileService.UpdateTokenByAccessKey(token, client.ResponseAccessKey);
UpdateStatusByCreatedClient(client);

// dispose client
client.StateChanged -= Client_StateChanged;
Expand All @@ -636,6 +630,8 @@ await ConnectInternal(token,
return;
}

// save lastSessionStatus before destroying the client
_lastSessionStatus = client.SessionStatus;
throw;
}
catch (Exception) {
Expand All @@ -644,6 +640,16 @@ await ConnectInternal(token,
}
}

private void UpdateStatusByCreatedClient(VpnHoodClient client)
{
// update access token if ResponseAccessKey is set
if (!string.IsNullOrWhiteSpace(client.ResponseAccessKey))
ClientProfileService.UpdateTokenByAccessKey(client.Token.TokenId, client.ResponseAccessKey);

if (client.ClientCountry != null)
_appPersistState.ClientCountryCodeByServer = client.ClientCountry;
}

private async Task RequestFeatures(CancellationToken cancellationToken)
{
// QuickLaunch
Expand Down Expand Up @@ -680,7 +686,7 @@ await Services.UiProvider.RequestNotification(ActiveUiContext.RequiredContext, c
}

private CultureInfo? _systemUiCulture;
public CultureInfo SystemUiCulture =>
public CultureInfo SystemUiCulture =>
_systemUiCulture ?? new CultureInfo(Services.CultureProvider.SystemCultures.FirstOrDefault() ?? CultureInfo.InstalledUICulture.Name);

private void InitCulture()
Expand All @@ -705,9 +711,10 @@ private void SettingsBeforeSave(object sender, EventArgs e)
ApplySettings();
}

public string GetCurrentCountryCode() => _appPersistState.ClientCountryCode ?? RegionInfo.CurrentRegion.Name;
public string GetClientCountry() => _appPersistState.ClientCountryCode ?? RegionInfo.CurrentRegion.Name;
public string GetClientCountryByServer() => _appPersistState.ClientCountryCodeByServer ?? GetClientCountry();

public async Task<string> GetCurrentCountryCodeAsync(CancellationToken cancellationToken)
public async Task<string> GetCurrentCountryAsync(CancellationToken cancellationToken)
{
_isFindingCountryCode = true;

Expand Down Expand Up @@ -893,7 +900,7 @@ await File.WriteAllTextAsync(VersionCheckFilePath, JsonSerializer.Serialize(_ver
}
}

public async Task<IpRangeOrderedList?> GetIncludeIpRanges(IPAddress clientIp, CancellationToken cancellationToken)
public async Task<IpRangeOrderedList> GetIncludeIpRanges(CancellationToken cancellationToken)
{
// calculate packetCaptureIpRanges
var ipRanges = IpNetwork.All.ToIpRanges();
Expand All @@ -911,11 +918,9 @@ await File.WriteAllTextAsync(VersionCheckFilePath, JsonSerializer.Serialize(_ver
if (!_useInternalLocationService)
throw new InvalidOperationException("Could not use internal location service because it is disabled.");

VhLogger.Instance.LogInformation("Loading country IP ranges. ClientIp: {ClientIp}", VhLogger.Format(clientIp));
var ipLocation = await IpRangeLocationProvider.GetLocation(clientIp, cancellationToken).VhConfigureAwait();
var countryIpRanges = await IpRangeLocationProvider.GetIpRanges(ipLocation.CountryCode).VhConfigureAwait();
UpdateCurrentCountry(ipLocation.CountryCode);
VhLogger.Instance.LogInformation("Client CountryCode is: {CountryCode}", _appPersistState.ClientCountryName);
var countryCode = await GetCurrentCountryAsync(cancellationToken).VhConfigureAwait();
var countryIpRanges = await IpRangeLocationProvider.GetIpRanges(countryCode).VhConfigureAwait();
VhLogger.Instance.LogInformation("Client CountryCode is: {CountryCode}", VhUtil.TryGetCountryName(_appPersistState.ClientCountryCode));
ipRanges = ipRanges.Exclude(countryIpRanges);
}
catch (Exception ex) {
Expand Down
9 changes: 0 additions & 9 deletions VpnHood.Client/Abstractions/IIpRangeProvider.cs

This file was deleted.

2 changes: 1 addition & 1 deletion VpnHood.Client/ClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public class ClientOptions
public Version Version { get; set; } = typeof(ClientOptions).Assembly.GetName().Version;
public bool UseUdpChannel { get; set; }
public bool IncludeLocalNetwork { get; set; }
public IIpRangeProvider? IpRangeProvider { get; set; }
public IAdService? AdService { get; set; }
public IpRangeOrderedList IncludeIpRanges { get; set; } = new(IpNetwork.All.ToIpRanges());
public IpRangeOrderedList PacketCaptureIncludeIpRanges { get; set; } = new(IpNetwork.All.ToIpRanges());
public SocketFactory SocketFactory { get; set; } = new();
public int MaxDatagramChannelCount { get; set; } = 4;
Expand Down
Loading

0 comments on commit b34796d

Please sign in to comment.