diff --git a/CHANGELOG.md b/CHANGELOG.md
index ddbf78585..9bf37f3b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+# v2.6.327
+### Server
+* Fix: Error on centos
+* Feature: Report CPU usage to access server
+* Feature: Add TcpConnectWait control
+* Feature: Add TcpChannelCount control
+
# v2.6.326
### Client
* Feature: Windows: Compile as Win-x64. NET runtime is not required anymore.
diff --git a/Pub/PublishApps.ps1 b/Pub/PublishApps.ps1
index b74e8ce46..ce1ca041c 100644
--- a/Pub/PublishApps.ps1
+++ b/Pub/PublishApps.ps1
@@ -7,7 +7,6 @@ param(
[Parameter(Mandatory=$true)][object]$distribute
);
-$bump = $bump -eq "1";
$nugets = $nugets -eq "1";
$android = $android -eq "1";
$distribute = $distribute -eq "1";
diff --git a/Pub/PublishToGitHub.ps1 b/Pub/PublishToGitHub.ps1
index 69e36dce5..de954396a 100644
--- a/Pub/PublishToGitHub.ps1
+++ b/Pub/PublishToGitHub.ps1
@@ -48,7 +48,7 @@ gh release create "$versionTag" `
-F $releaseRootDir/ReleaseNote.txt `
$releaseClientDir/android/VpnHoodClient-Android.apk `
$releaseClientDir/windows/VpnHoodClient-win-x64.exe `
- $releaseClientDir/windows/VpnHoodClient-win.txt `
+ $packagesRootDir/../Latest/Client/windows/VpnHoodClient-win.txt `
$releaseClientDir/windows/VpnHoodClient-win-x64.txt `
$releaseServerDir/linux-x64/VpnHoodServer-linux-x64.json `
$releaseServerDir/linux-x64/VpnHoodServer-linux-x64.sh `
diff --git a/Pub/Version.json b/Pub/Version.json
index 97a98f19c..e22be8035 100644
--- a/Pub/Version.json
+++ b/Pub/Version.json
@@ -1,7 +1,7 @@
{
"Major": 2,
"Minor": 6,
- "Build": 326,
- "BumpTime": "2022-12-15T04:41:17.2014844Z",
+ "Build": 328,
+ "BumpTime": "2022-12-19T10:20:18.3580504Z",
"Prerelease": false
}
diff --git a/Samples/VpnHood.Samples.SimpleClient.Droid/VpnHood.Samples.SimpleClient.Droid.csproj b/Samples/VpnHood.Samples.SimpleClient.Droid/VpnHood.Samples.SimpleClient.Droid.csproj
index 79a700b48..2f3688fd3 100644
--- a/Samples/VpnHood.Samples.SimpleClient.Droid/VpnHood.Samples.SimpleClient.Droid.csproj
+++ b/Samples/VpnHood.Samples.SimpleClient.Droid/VpnHood.Samples.SimpleClient.Droid.csproj
@@ -108,10 +108,10 @@
- 2.5.324
+ 2.6.327
- 2.5.324
+ 2.6.327
diff --git a/Samples/VpnHood.Samples.SimpleClient.Win/VpnHood.Samples.SimpleClient.Win.csproj b/Samples/VpnHood.Samples.SimpleClient.Win/VpnHood.Samples.SimpleClient.Win.csproj
index bc07e2efe..1009381a1 100644
--- a/Samples/VpnHood.Samples.SimpleClient.Win/VpnHood.Samples.SimpleClient.Win.csproj
+++ b/Samples/VpnHood.Samples.SimpleClient.Win/VpnHood.Samples.SimpleClient.Win.csproj
@@ -11,8 +11,8 @@
-
-
+
+
diff --git a/VpnHood.Client.App.Android/Properties/AndroidManifest.xml b/VpnHood.Client.App.Android/Properties/AndroidManifest.xml
index be55375c8..96c071c07 100644
--- a/VpnHood.Client.App.Android/Properties/AndroidManifest.xml
+++ b/VpnHood.Client.App.Android/Properties/AndroidManifest.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/VpnHood.Client.App.UI/VpnHood.Client.App.UI.csproj b/VpnHood.Client.App.UI/VpnHood.Client.App.UI.csproj
index 9dc487db0..f5de5eed2 100644
--- a/VpnHood.Client.App.UI/VpnHood.Client.App.UI.csproj
+++ b/VpnHood.Client.App.UI/VpnHood.Client.App.UI.csproj
@@ -12,9 +12,9 @@
VpnHood.png
Tiny internal webserver to server your single-page application (SPA). You need this only if you want to create a UI for your VpnHood client by single-page application (SPA).
VpnHood.Client.App.UI
- 2.6.326
- 2.6.326
- 2.6.326
+ 2.6.328
+ 2.6.328
+ 2.6.328
enable
latest
diff --git a/VpnHood.Client.App.Win.Setup/VpnHood.Client.App.Win.Setup.back (1).aip b/VpnHood.Client.App.Win.Setup/VpnHood.Client.App.Win.Setup.back (1).aip
new file mode 100644
index 000000000..8f8b54d72
--- /dev/null
+++ b/VpnHood.Client.App.Win.Setup/VpnHood.Client.App.Win.Setup.back (1).aip
@@ -0,0 +1,1860 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VpnHood.Client.App.Win/VpnHood.Client.App.Win.csproj b/VpnHood.Client.App.Win/VpnHood.Client.App.Win.csproj
index 088a42173..8c33f214e 100644
--- a/VpnHood.Client.App.Win/VpnHood.Client.App.Win.csproj
+++ b/VpnHood.Client.App.Win/VpnHood.Client.App.Win.csproj
@@ -19,11 +19,11 @@
VpnHood.png
VpnHood.Client.App.Win
- 2.6.326
- 2.6.326
- 2.6.326
+ 2.6.328
+ 2.6.328
+ 2.6.328
enable
- latest
+ 11
false
7.0
False
diff --git a/VpnHood.Client.App.Win/WebViewWindow.cs b/VpnHood.Client.App.Win/WebViewWindow.cs
index 6a3fe6ec9..1281fd63a 100644
--- a/VpnHood.Client.App.Win/WebViewWindow.cs
+++ b/VpnHood.Client.App.Win/WebViewWindow.cs
@@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Drawing;
+using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
@@ -69,6 +70,11 @@ private void InitWebView(Uri url, string dataFolderPath)
((System.ComponentModel.ISupportInitialize)(_webView)).EndInit();
}
+ public Task EnsureCoreWebView2Async()
+ {
+ return _webView.EnsureCoreWebView2Async();
+ }
+
private void UpdatePosition()
{
// body
@@ -102,7 +108,7 @@ private void WebView_CoreWebView2InitializationCompleted(object? sender, EventAr
{
if (sender is WebView2 webView)
{
- webView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
+ webView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = true;
webView.CoreWebView2.NewWindowRequested += CoreWebView2_NewWindowRequested;
}
}
diff --git a/VpnHood.Client.App.Win/WinApp.cs b/VpnHood.Client.App.Win/WinApp.cs
index 47d00eb60..03b5006f3 100644
--- a/VpnHood.Client.App.Win/WinApp.cs
+++ b/VpnHood.Client.App.Win/WinApp.cs
@@ -16,9 +16,9 @@ namespace VpnHood.Client.App;
public class WinApp : AppBaseNet
{
private const string FileNameAppCommand = "appcommand";
- private readonly System.Windows.Forms.Timer _uiTimer;
+ private readonly Timer _uiTimer;
private DateTime _lastUpdateTime = DateTime.MinValue;
- private System.Windows.Forms.NotifyIcon? _notifyIcon;
+ private NotifyIcon? _notifyIcon;
private readonly CommandListener _commandListener;
private WebViewWindow? _webViewWindow;
private string AppLocalDataPath { get; }
@@ -26,7 +26,7 @@ public class WinApp : AppBaseNet
public WinApp() : base("VpnHood")
{
//init timer
- _uiTimer = new System.Windows.Forms.Timer
+ _uiTimer = new Timer
{
Interval = 1000
};
@@ -133,11 +133,21 @@ public void InitWevView()
{
if (!WebViewWindow.IsInstalled) return;
_webViewWindow = new WebViewWindow(new Uri(VhAppUi.Url), Path.Combine(VhApp.AppDataFolderPath, "Temp"));
+ _webViewWindow.EnsureCoreWebView2Async()
+ .ContinueWith(x =>
+ {
+ if (x.IsFaulted)
+ {
+ _webViewWindow?.Close();
+ _webViewWindow = null;
+ }
+ });
//_webViewWindow.Init("https://www.google.com", @"C:\Users\Developer\AppData\Local\VpnHood\Temp\dd").Wait();
}
catch (Exception ex)
{
+ _webViewWindow = null;
VhLogger.Instance.LogWarning($"Could not use WebView. Using the default browser. {ex.Message}");
}
}
@@ -207,17 +217,17 @@ public void OpenMainWindow()
private void InitNotifyIcon()
{
- _notifyIcon = new System.Windows.Forms.NotifyIcon
+ _notifyIcon = new NotifyIcon
{
Icon = Resource.VpnHoodIcon
};
_notifyIcon.MouseClick += (_, e) =>
{
- if (e.Button == System.Windows.Forms.MouseButtons.Left)
+ if (e.Button == MouseButtons.Left)
OpenMainWindow();
};
- var menu = new System.Windows.Forms.ContextMenuStrip();
+ var menu = new ContextMenuStrip();
menu.Items.Add(AppUiResource.Open, null, (_, _) => OpenMainWindow());
menu.Items.Add("-");
@@ -256,7 +266,7 @@ private void ConnectMenuItem_Click(object? sender, EventArgs e)
private void Menu_Opening(object? sender, CancelEventArgs e)
{
- var menu = (System.Windows.Forms.ContextMenuStrip)sender!;
+ var menu = (ContextMenuStrip)sender!;
menu.Items["connect"].Enabled = VhApp.IsIdle;
menu.Items["disconnect"].Enabled =
!VhApp.IsIdle && VhApp.State.ConnectionState != AppConnectionState.Disconnecting;
diff --git a/VpnHood.Client.App/VpnHood.Client.App.csproj b/VpnHood.Client.App/VpnHood.Client.App.csproj
index 4cc2d0646..a9bce033e 100644
--- a/VpnHood.Client.App/VpnHood.Client.App.csproj
+++ b/VpnHood.Client.App/VpnHood.Client.App.csproj
@@ -11,9 +11,9 @@
https://github.com/vpnhood/vpnhood
Readymade Vpn App skeleton for VpnHood clients. You just need to create a UI on it.
VpnHood.Client.App
- 2.6.326
- 2.6.326
- 2.6.326
+ 2.6.328
+ 2.6.328
+ 2.6.328
enable
latest
diff --git a/VpnHood.Client.Device.WinDivert/VpnHood.Client.Device.WinDivert.csproj b/VpnHood.Client.Device.WinDivert/VpnHood.Client.Device.WinDivert.csproj
index 8780b2c7d..20345c29e 100644
--- a/VpnHood.Client.Device.WinDivert/VpnHood.Client.Device.WinDivert.csproj
+++ b/VpnHood.Client.Device.WinDivert/VpnHood.Client.Device.WinDivert.csproj
@@ -9,11 +9,11 @@
https://github.com/vpnhood/vpnhood
VpnHood.png
VpnHood client device provider for Windows using WinDivert.
- 2.6.326
+ 2.6.328
VpnHood.Client.Device.WinDivert
1.1.226
- 2.6.326
- 2.6.326
+ 2.6.328
+ 2.6.328
enable
latest
diff --git a/VpnHood.Client.Device/VpnHood.Client.Device.csproj b/VpnHood.Client.Device/VpnHood.Client.Device.csproj
index cf237a004..db7117ac9 100644
--- a/VpnHood.Client.Device/VpnHood.Client.Device.csproj
+++ b/VpnHood.Client.Device/VpnHood.Client.Device.csproj
@@ -14,9 +14,9 @@
VpnHood.Client.Device
VpnHood.Client.Device
- 2.6.326
- 2.6.326
- 2.6.326
+ 2.6.328
+ 2.6.328
+ 2.6.328
enable
latest
diff --git a/VpnHood.Client/VpnHood.Client.csproj b/VpnHood.Client/VpnHood.Client.csproj
index b4c76ffed..c3c58d379 100644
--- a/VpnHood.Client/VpnHood.Client.csproj
+++ b/VpnHood.Client/VpnHood.Client.csproj
@@ -13,9 +13,9 @@
2022 VpnHood
VpnHood.Client
VPN VpnClient Proxy
- 2.6.326
- 2.6.326
- 2.6.326
+ 2.6.328
+ 2.6.328
+ 2.6.328
enable
latest
diff --git a/VpnHood.Common/Util.cs b/VpnHood.Common/Util.cs
index 6ab46144a..e1930c16a 100644
--- a/VpnHood.Common/Util.cs
+++ b/VpnHood.Common/Util.cs
@@ -197,4 +197,48 @@ public static string RedactIpAddress(IPAddress ipAddress)
return ipAddress.ToString();
}
+
+ public static string FormatBytes(long size)
+ {
+ // Get absolute value
+ if (size >= 0x10000000000) // Terabyte
+ return ((double)(size >> 30) / 1024).ToString("0.## ") + "TB";
+
+ if (size >= 0x40000000) // Gigabyte
+ return ((double)(size >> 20) / 1024).ToString("0.# ") + "GB";
+
+ if (size >= 0x100000) // Megabyte
+ return ((double)(size >> 10) / 1024).ToString("0 ") + "MB";
+
+ if (size >= 1024) // Kilobyte
+ return ((double)size / 1024).ToString("0 ") + "KB";
+
+ if (size > 0) // Kilobyte
+ return size.ToString("0 ") + "B";
+
+ // Byte
+ return size.ToString("0");
+ }
+
+ [SuppressMessage("ReSharper", "PossibleLossOfFraction")]
+ public static string FormatBits(long bytes)
+ {
+ bytes *= 8; //convertTo bit
+
+ // Get absolute value
+ if (bytes >= 0x40000000) // Gigabyte
+ return ((double)(bytes / 0x40000000)).ToString("0.# ") + "Gbps";
+
+ if (bytes >= 0x100000) // Megabyte
+ return ((double)(bytes / 0x100000)).ToString("0 ") + "Mbps";
+
+ if (bytes >= 1024) // Kilobyte
+ return ((double)(bytes / 1024)).ToString("0 ") + "Kbps";
+
+ if (bytes > 0) // Kilobyte
+ return ((double)bytes).ToString("0 ") + "bps";
+
+ // Byte
+ return bytes.ToString("0");
+ }
}
\ No newline at end of file
diff --git a/VpnHood.Common/VpnHood.Common.csproj b/VpnHood.Common/VpnHood.Common.csproj
index 77b80cbaa..ff1ce36c5 100644
--- a/VpnHood.Common/VpnHood.Common.csproj
+++ b/VpnHood.Common/VpnHood.Common.csproj
@@ -12,9 +12,9 @@
VpnHood.Common
VpnHood.png
VpnHood Common Library is shared among all other VpnHood modules.
- 2.6.326
- 2.6.326
- 2.6.326
+ 2.6.328
+ 2.6.328
+ 2.6.328
enable
latest
diff --git a/VpnHood.Server.Access/ServerInfo.cs b/VpnHood.Server.Access/ServerInfo.cs
index bbf3c7f35..e4b75e1cc 100644
--- a/VpnHood.Server.Access/ServerInfo.cs
+++ b/VpnHood.Server.Access/ServerInfo.cs
@@ -35,7 +35,8 @@ public ServerInfo(Version version,
public ServerStatus Status { get; set; }
public string? OsInfo { get; set; }
public string? OsVersion { get; set; }
- public long TotalMemory { get; set; }
+ public long? TotalMemory { get; set; }
public string? MachineName { get; set; }
public string? LastError { get; set; }
+ public int LogicalCoreCount { get; set; }
}
\ No newline at end of file
diff --git a/VpnHood.Server.Access/ServerStatus.cs b/VpnHood.Server.Access/ServerStatus.cs
index 2bc21dab2..cae71cf24 100644
--- a/VpnHood.Server.Access/ServerStatus.cs
+++ b/VpnHood.Server.Access/ServerStatus.cs
@@ -5,7 +5,8 @@ public class ServerStatus
public int SessionCount { get; set; }
public int TcpConnectionCount { get; set; }
public int UdpConnectionCount { get; set; }
- public long FreeMemory { get; set; }
+ public long? AvailableMemory { get; set; }
+ public int? CpuUsage { get; set; }
public long UsedMemory { get; set; }
public int ThreadCount { get; set; }
public long TunnelSendSpeed { get; set; }
diff --git a/VpnHood.Server.Access/VpnHood.Server.Access.csproj b/VpnHood.Server.Access/VpnHood.Server.Access.csproj
index 1adb97a61..3919b9dd9 100644
--- a/VpnHood.Server.Access/VpnHood.Server.Access.csproj
+++ b/VpnHood.Server.Access/VpnHood.Server.Access.csproj
@@ -12,9 +12,9 @@
VpnHood.Server.Access
VpnHood.png
Stores, and retrieves end users' access and usage. Provides required interfaces and classes to use or create an access server and accounting.
- 2.6.326
- 2.6.326
- 2.6.326
+ 2.6.328
+ 2.6.328
+ 2.6.328
enable
latest
diff --git a/VpnHood.Server.App.Net/AppSettings.cs b/VpnHood.Server.App.Net/AppSettings.cs
index 51e534d58..423f4ba32 100644
--- a/VpnHood.Server.App.Net/AppSettings.cs
+++ b/VpnHood.Server.App.Net/AppSettings.cs
@@ -12,4 +12,7 @@ public class AppSettings
public FileAccessServerOptions? FileAccessServer { get; set; } = new();
public bool IsAnonymousTrackerEnabled { get; set; } = true;
public bool IsDiagnoseMode { get; set; }
+ public int MaxTcpConnectWaitCount { get; set; } = new ServerOptions().MaxTcpConnectWaitCount;
+ public int MaxTcpChannelCount { get; set; } = new ServerOptions().MaxTcpChannelCount;
+ public TimeSpan TcpConnectTimeout { get; set; } = new ServerOptions().TcpConnectTimeout;
}
\ No newline at end of file
diff --git a/VpnHood.Server.App.Net/Program.cs b/VpnHood.Server.App.Net/Program.cs
index 92747f664..5e937e207 100644
--- a/VpnHood.Server.App.Net/Program.cs
+++ b/VpnHood.Server.App.Net/Program.cs
@@ -13,7 +13,7 @@ private static void Main(string[] args)
}
catch (Exception ex)
{
- Console.WriteLine(ex.Message);
+ Console.WriteLine(ex);
}
}
}
\ No newline at end of file
diff --git a/VpnHood.Server.App.Net/Properties/launchSettings.json b/VpnHood.Server.App.Net/Properties/launchSettings.json
index a0617b72b..4b3167a0f 100644
--- a/VpnHood.Server.App.Net/Properties/launchSettings.json
+++ b/VpnHood.Server.App.Net/Properties/launchSettings.json
@@ -1,8 +1,7 @@
{
"profiles": {
"VpnHood.Server.App.Net": {
- "commandName": "Project",
- "commandLineArgs": "gen -domain"
+ "commandName": "Project"
},
"WSL 2": {
"commandName": "WSL2",
diff --git a/VpnHood.Server.App.Net/ServerApp.cs b/VpnHood.Server.App.Net/ServerApp.cs
index 0ceee84b5..b9314fb12 100644
--- a/VpnHood.Server.App.Net/ServerApp.cs
+++ b/VpnHood.Server.App.Net/ServerApp.cs
@@ -2,7 +2,6 @@
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
-using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.Logging;
using NLog;
@@ -46,7 +45,7 @@ public ServerApp() : base("VpnHoodServer")
: Path.Combine(Directory.GetCurrentDirectory(), FolderNameStorage);
Directory.CreateDirectory(storagePath);
Directory.SetCurrentDirectory(storagePath);
-
+
// internal folder
InternalStoragePath = Path.Combine(storagePath, FolderNameInternal);
Directory.CreateDirectory(InternalStoragePath);
@@ -181,12 +180,12 @@ private bool IsAnotherInstanceRunning()
try
{
_lockStream = File.OpenWrite(lockFile);
- var stream =new StreamWriter(_lockStream, leaveOpen: true);
+ var stream = new StreamWriter(_lockStream, leaveOpen: true);
stream.WriteLine(DateTime.UtcNow);
stream.Dispose();
return false;
}
- catch (IOException)
+ catch (IOException)
{
return true;
}
@@ -223,7 +222,10 @@ private void StartServer(CommandLineApplication cmdApp)
Tracker = _googleAnalytics,
SystemInfoProvider = systemInfoProvider,
SocketFactory = new ServerSocketFactory(),
- StoragePath = InternalStoragePath
+ StoragePath = InternalStoragePath,
+ TcpConnectTimeout = AppSettings.TcpConnectTimeout,
+ MaxTcpConnectWaitCount = AppSettings.MaxTcpConnectWaitCount,
+ MaxTcpChannelCount = AppSettings.MaxTcpChannelCount
});
// track
diff --git a/VpnHood.Server.App.Net/SystemInformation/LinuxSystemInfoProvider.cs b/VpnHood.Server.App.Net/SystemInformation/LinuxSystemInfoProvider.cs
index 8a2eb8f52..1f563c066 100644
--- a/VpnHood.Server.App.Net/SystemInformation/LinuxSystemInfoProvider.cs
+++ b/VpnHood.Server.App.Net/SystemInformation/LinuxSystemInfoProvider.cs
@@ -1,70 +1,103 @@
-using System;
+using Microsoft.Extensions.Logging;
+using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
-using System.Text.RegularExpressions;
+using VpnHood.Common.Logging;
using VpnHood.Server.SystemInformation;
namespace VpnHood.Server.App.SystemInformation;
public class LinuxSystemInfoProvider : ISystemInfoProvider
{
- public SystemInfo GetSystemInfo()
+ private long? GetMemInfoValue(string key)
{
- long totalMemory = 0;
- long freeMemory = 0;
- long buffers = 0;
- long cached = 0;
+ try
+ {
+ var meminfo = File.ReadAllText("/proc/meminfo");
+ var memTotalLine = meminfo.Split('\n').FirstOrDefault(line => line.StartsWith($"{key}:"));
+ if (memTotalLine == null)
+ return null;
- string[] memInfoLines = File.ReadAllLines(@"/proc/meminfo");
+ var tokenize = memTotalLine.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
+ if (tokenize.Length < 1)
+ return null;
- MemInfoMatch[] memInfoMatches =
- {
- new(@"^Buffers:\s+(\d+)", value => buffers = Convert.ToInt64(value)),
- new(@"^Cached:\s+(\d+)", value => cached = Convert.ToInt64(value)),
- new(@"^MemFree:\s+(\d+)", value => freeMemory = Convert.ToInt64(value)),
- new(@"^MemTotal:\s+(\d+)", value => totalMemory = Convert.ToInt64(value))
- };
+ return long.Parse(tokenize[1]) * 1000;
- foreach (var memInfoLine in memInfoLines)
- foreach (var memInfoMatch in memInfoMatches)
+ }
+ catch (Exception ex)
{
- Match match = memInfoMatch.Regex.Match(memInfoLine);
- if (match.Groups[1].Success)
- {
- var value = match.Groups[1].Value;
- memInfoMatch.UpdateValue(value);
- }
+ VhLogger.Instance.LogWarning(ex, $"Could not read {key} form /proc/meminfo.");
+ return null;
}
-
- return new SystemInfo(
- totalMemory * 1000,
- (freeMemory + cached + buffers) * 1000);
}
- public string GetOperatingSystemInfo()
+ private int? GetCpuUsage()
{
+ try
+ {
+ // Read the first line of the /proc/stat file
+ var statLine = File.ReadAllLines("/proc/stat")[0];
+
+ // Split the line into fields
+ var fields = statLine
+ .Split(' ')
+ .Where(x => x.Trim() != "").ToArray();
+
+ // Parse the fields
+ var userTime = long.Parse(fields[1]);
+ var niceTime = long.Parse(fields[2]);
+ var systemTime = long.Parse(fields[3]);
+ var idleTime = long.Parse(fields[4]);
+ var ioWaitTime = long.Parse(fields[5]);
+ var irqTime = long.Parse(fields[6]);
+ var softIrqTime = long.Parse(fields[7]);
- var items = File
- .ReadAllLines("/etc/os-release")
- .Select(line => line.Split('='))
- .ToDictionary(split => split[0], split => split[1]);
+ // Calculate the total CPU time
+ var totalTime = userTime + niceTime + systemTime + idleTime + ioWaitTime + irqTime + softIrqTime;
- var ret = items["PRETTY_NAME"].Replace("\"", "");
- ret += $", {RuntimeInformation.OSArchitecture}";
+ // Calculate the CPU usage
+ var usage = 100.0 * (totalTime - idleTime) / totalTime;
+ return (int)usage;
- return ret.Trim();
+ }
+ catch (Exception ex)
+ {
+ VhLogger.Instance.LogWarning(ex, "Could not read CPU usage form /proc/stat.");
+ return null;
+ }
}
- private class MemInfoMatch
+ public SystemInfo GetSystemInfo()
{
- public MemInfoMatch(string pattern, Action update)
+ var ret = new SystemInfo(
+ GetOperatingSystemInfo(),
+ GetMemInfoValue("MemTotal"),
+ GetMemInfoValue("MemAvailable"),
+ GetCpuUsage(),
+ Environment.ProcessorCount
+ );
+ return ret;
+ }
+
+ public string GetOperatingSystemInfo()
+ {
+ string? prettyName = null;
+ if (File.Exists("/etc/os-release"))
{
- Regex = new Regex(pattern, RegexOptions.Compiled);
- UpdateValue = update;
+ var items = File
+ .ReadAllLines("/etc/os-release")
+ .Select(line => line.Split('='))
+ .Where(split => split.Length >= 1 && !string.IsNullOrEmpty(split[0]))
+ .ToDictionary(split => split[0], split => split[1]);
+
+ if (items.TryGetValue("PRETTY_NAME", out prettyName))
+ prettyName = prettyName.Replace("\"", "").Trim();
}
- public Regex Regex { get; }
- public Action UpdateValue { get; }
+ if (string.IsNullOrEmpty(prettyName)) prettyName = "Linux";
+ prettyName += $", {RuntimeInformation.OSArchitecture}";
+ return prettyName.Trim();
}
}
\ No newline at end of file
diff --git a/VpnHood.Server.App.Net/SystemInformation/WinSystemInfoProvider.cs b/VpnHood.Server.App.Net/SystemInformation/WinSystemInfoProvider.cs
index ff99ededb..52524ce3c 100644
--- a/VpnHood.Server.App.Net/SystemInformation/WinSystemInfoProvider.cs
+++ b/VpnHood.Server.App.Net/SystemInformation/WinSystemInfoProvider.cs
@@ -1,6 +1,10 @@
using System;
using System.Runtime.InteropServices;
using VpnHood.Server.SystemInformation;
+using System.Diagnostics;
+using VpnHood.Common.Logging;
+using Microsoft.Extensions.Logging;
+#pragma warning disable CA1416
// ReSharper disable MemberCanBePrivate.Local
// ReSharper disable StructCanBeMadeReadOnly
@@ -9,46 +13,34 @@ namespace VpnHood.Server.App.SystemInformation;
public class WinSystemInfoProvider : ISystemInfoProvider
{
- [StructLayout(LayoutKind.Sequential)]
- private struct PerformanceInformation
- {
- public readonly int Size;
- public readonly IntPtr CommitTotal;
- public readonly IntPtr CommitLimit;
- public readonly IntPtr CommitPeak;
- public readonly IntPtr PhysicalTotal;
- public readonly IntPtr PhysicalAvailable;
- public readonly IntPtr SystemCache;
- public readonly IntPtr KernelTotal;
- public readonly IntPtr KernelPaged;
- public readonly IntPtr KernelNonPaged;
- public readonly IntPtr PageSize;
- public readonly int HandlesCount;
- public readonly int ProcessCount;
- public readonly int ThreadCount;
- }
+ private readonly PerformanceCounter _cpuCounter = new ("Processor", "% Processor Time", "_Total");
+
public SystemInfo GetSystemInfo()
{
- long totalMemory = 0;
- long freeMemory = 0;
+ try
+ {
- if (GetPerformanceInfo(out var pi, Marshal.SizeOf()))
+ var availableMemoryCounter = new PerformanceCounter("Memory", "Available Bytes");
+ var availableMemory = availableMemoryCounter.RawValue;
+ var totalMemory = GC.GetGCMemoryInfo().TotalAvailableMemoryBytes;
+ var usage = _cpuCounter.NextValue();
+ return new SystemInfo(GetOperatingSystemInfo(),
+ totalMemory, availableMemory,
+ (int)usage,Environment.ProcessorCount);
+
+ }
+ catch (Exception ex)
{
- freeMemory = Convert.ToInt64(pi.PhysicalAvailable.ToInt64() * pi.PageSize.ToInt64());
- totalMemory = Convert.ToInt64(pi.PhysicalTotal.ToInt64() * pi.PageSize.ToInt64());
+ VhLogger.Instance.LogWarning(ex, "Could not get SystemInfo.");
+ return new SystemInfo(GetOperatingSystemInfo(),
+ 0, 0,
+ 0, Environment.ProcessorCount);
}
- return new SystemInfo(totalMemory, freeMemory);
}
- public string GetOperatingSystemInfo()
+ public static string GetOperatingSystemInfo()
{
- return $"{Environment.OSVersion}, {RuntimeInformation.OSDescription}";
+ return RuntimeInformation.OSDescription.Replace("Microsoft", "").Trim();
}
-
- // ReSharper disable once StringLiteralTypo
- [DllImport("psapi.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool GetPerformanceInfo([Out] out PerformanceInformation performanceInformation,
- [In] int size);
}
\ No newline at end of file
diff --git a/VpnHood.Server.App.Net/VpnHood.Server.App.Net.csproj b/VpnHood.Server.App.Net/VpnHood.Server.App.Net.csproj
index 152ea729d..12e19bbe6 100644
--- a/VpnHood.Server.App.Net/VpnHood.Server.App.Net.csproj
+++ b/VpnHood.Server.App.Net/VpnHood.Server.App.Net.csproj
@@ -12,9 +12,9 @@
https://github.com/vpnhood/vpnhood
LGPL-2.1-only
VpnHood.png
- 2.6.326
- 2.6.326
- 2.6.326
+ 2.6.328
+ 2.6.328
+ 2.6.328
enable
latest
Linux
@@ -43,6 +43,7 @@
+
diff --git a/VpnHood.Server.App.Net/nlog.config b/VpnHood.Server.App.Net/nlog.config
index cf81203cd..68b217efd 100644
--- a/VpnHood.Server.App.Net/nlog.config
+++ b/VpnHood.Server.App.Net/nlog.config
@@ -39,7 +39,7 @@
-
+
diff --git a/VpnHood.Server/Exceptions/MaxTcpChannelException.cs b/VpnHood.Server/Exceptions/MaxTcpChannelException.cs
new file mode 100644
index 000000000..79e4b7d0a
--- /dev/null
+++ b/VpnHood.Server/Exceptions/MaxTcpChannelException.cs
@@ -0,0 +1,12 @@
+using VpnHood.Common.Exceptions;
+using VpnHood.Common.Messaging;
+
+namespace VpnHood.Server.Exceptions;
+
+internal class MaxTcpChannelException : SessionException
+{
+ public MaxTcpChannelException(uint sessionId)
+ : base(SessionErrorCode.GeneralError, $"Maximum TcpChannel has been reached. SessionId: {sessionId}.")
+ {
+ }
+}
\ No newline at end of file
diff --git a/VpnHood.Server/Exceptions/MaxTcpConnectException.cs b/VpnHood.Server/Exceptions/MaxTcpConnectException.cs
new file mode 100644
index 000000000..0540365f6
--- /dev/null
+++ b/VpnHood.Server/Exceptions/MaxTcpConnectException.cs
@@ -0,0 +1,12 @@
+using VpnHood.Common.Exceptions;
+using VpnHood.Common.Messaging;
+
+namespace VpnHood.Server.Exceptions;
+
+internal class MaxTcpConnectException : SessionException
+{
+ public MaxTcpConnectException(uint sessionId)
+ : base(SessionErrorCode.GeneralError, $"Maximum TcpConnectWait has been reached. SessionId: {sessionId}.")
+ {
+ }
+}
\ No newline at end of file
diff --git a/VpnHood.Server/Exceptions/TlsAuthenticateException.cs b/VpnHood.Server/Exceptions/TlsAuthenticateException.cs
new file mode 100644
index 000000000..1af8dfcb2
--- /dev/null
+++ b/VpnHood.Server/Exceptions/TlsAuthenticateException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace VpnHood.Server.Exceptions;
+
+internal class TlsAuthenticateException : Exception
+{
+ public TlsAuthenticateException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+}
\ No newline at end of file
diff --git a/VpnHood.Server/ServerOptions.cs b/VpnHood.Server/ServerOptions.cs
index e74ffdc55..55bc5f0ce 100644
--- a/VpnHood.Server/ServerOptions.cs
+++ b/VpnHood.Server/ServerOptions.cs
@@ -15,4 +15,7 @@ public class ServerOptions
public TimeSpan ConfigureInterval { get; set; } = TimeSpan.FromSeconds(60);
public string StoragePath { get; set; } = Directory.GetCurrentDirectory();
public bool PublicIpDiscovery { get; set; } = true;
+ public int MaxTcpConnectWaitCount { get; set; } = 500;
+ public int MaxTcpChannelCount { get; set; } = 1000;
+ public TimeSpan TcpConnectTimeout { get; set; } = TimeSpan.FromSeconds(60);
}
\ No newline at end of file
diff --git a/VpnHood.Server/Session.cs b/VpnHood.Server/Session.cs
index cf2f2063d..9eb34ace8 100644
--- a/VpnHood.Server/Session.cs
+++ b/VpnHood.Server/Session.cs
@@ -32,6 +32,7 @@ public class Session : IDisposable, IAsyncDisposable
private readonly Timer _cleanupTimer;
private DateTime _lastSyncedTime = DateTime.Now;
private readonly TrackingOptions _trackingOptions;
+ public int TcpConnectWaitCount;
public Tunnel Tunnel { get; }
public uint SessionId { get; }
@@ -39,14 +40,13 @@ public class Session : IDisposable, IAsyncDisposable
public ResponseBase SessionResponse { get; private set; }
public UdpChannel? UdpChannel { get; private set; }
public bool IsDisposed { get; private set; }
-
- public int TcpConnectionCount =>
- Tunnel.StreamChannelCount + (UseUdpChannel ? 0 : Tunnel.DatagramChannels.Length);
+ public int TcpChannelCount =>
+ Tunnel.StreamChannelCount + (UseUdpChannel ? 0 : Tunnel.DatagramChannels.Length);
+
public int UdpConnectionCount => _proxyManager.UdpConnectionCount + (UseUdpChannel ? 1 : 0);
public DateTime LastActivityTime => Tunnel.LastActivityTime;
-
internal Session(IAccessServer accessServer, SessionResponse sessionResponse, SocketFactory socketFactory,
IPEndPoint hostEndPoint, SessionOptions options, TrackingOptions trackingOptions)
{
@@ -66,8 +66,7 @@ internal Session(IAccessServer accessServer, SessionResponse sessionResponse, So
if (options.MaxDatagramChannelCount > 0) tunnelOptions.MaxDatagramChannelCount = options.MaxDatagramChannelCount;
Tunnel = new Tunnel(tunnelOptions);
Tunnel.OnPacketReceived += Tunnel_OnPacketReceived;
- Tunnel.OnTrafficChanged += Tunnel_OnTrafficChanged;
-
+
if (trackingOptions.IsEnabled())
_proxyManager.OnNewEndPoint += OnNewEndPoint;
}
@@ -82,8 +81,8 @@ private void Cleanup(object state)
_proxyManager.Cleanup();
Tunnel.Cleanup();
- if (DateTime.Now - _lastSyncedTime > _syncInterval)
- _ = Sync();
+ var force = DateTime.Now - _lastSyncedTime > _syncInterval;
+ _ = Sync(force, false);
}
public bool UseUdpChannel
@@ -126,11 +125,6 @@ private void Tunnel_OnPacketReceived(object sender, ChannelPacketReceivedEventAr
_proxyManager.SendPacket(e.IpPackets);
}
- private void Tunnel_OnTrafficChanged(object sender, EventArgs e)
- {
- _ = Sync(false, false);
- }
-
public Task Sync() => Sync(true, false);
private async Task Sync(bool force, bool closeSession)
@@ -216,7 +210,6 @@ public async ValueTask DisposeAsync(bool closeSessionInAccessServer, bool log =
IsDisposed = true;
Tunnel.OnPacketReceived -= Tunnel_OnPacketReceived;
- Tunnel.OnTrafficChanged -= Tunnel_OnTrafficChanged;
Tunnel.Dispose();
_proxyManager.Dispose();
@@ -225,12 +218,8 @@ public async ValueTask DisposeAsync(bool closeSessionInAccessServer, bool log =
// Report removing session
if (log)
- {
- if (closeSessionInAccessServer)
- VhLogger.Instance.LogInformation(GeneralEventId.Session, "The session has been permanently closed.");
- else
- VhLogger.Instance.LogInformation(GeneralEventId.Session, "The session has been temporarily closed.");
- }
+ VhLogger.Instance.LogInformation(GeneralEventId.Session, "The session has been {State} closed. SessionId: {SessionId}.",
+ closeSessionInAccessServer ? "permanently" : "temporary", SessionId);
}
public void LogTrack(string protocol, int localPort, IPEndPoint destinationEndPoint)
@@ -243,8 +232,8 @@ public void LogTrack(string protocol, int localPort, IPEndPoint destinationEndPo
var destinationPortStr = _trackingOptions.TrackDestinationPort ? destinationEndPoint.Port.ToString() : "*";
VhLogger.Instance.LogInformation(GeneralEventId.Track,
- "Proto: {Proto}, SessionId: {SessionId}, TcpCount: {TcpCount}, UdpCount: {UdpCount}, SrcPort: {SrcPort}, DstIp:{DstIp}, DstPort: {DstPort}",
- protocol, SessionId, TcpConnectionCount, _proxyManager.UsedUdpPortCount,
+ "Proto: {Proto}, SessionId: {SessionId}, Tcp: {TcpCount}, Udp: {UdpCount}, TcpWait: {TcpConnectWaitCount}, SrcPort: {SrcPort}, DstIp:{DstIp}, DstPort: {DstPort}",
+ protocol, SessionId, TcpChannelCount, _proxyManager.UsedUdpPortCount, TcpConnectWaitCount,
localPortStr, destinationIpStr, destinationPortStr);
}
diff --git a/VpnHood.Server/SystemInformation/BasicSystemInfoProvider.cs b/VpnHood.Server/SystemInformation/BasicSystemInfoProvider.cs
index ef00283ff..eed2af52d 100644
--- a/VpnHood.Server/SystemInformation/BasicSystemInfoProvider.cs
+++ b/VpnHood.Server/SystemInformation/BasicSystemInfoProvider.cs
@@ -11,6 +11,7 @@ public string GetOperatingSystemInfo()
public SystemInfo GetSystemInfo()
{
- return new SystemInfo(0, 0);
+ return new SystemInfo(Environment.OSVersion.ToString(),
+ null, null, 0, Environment.ProcessorCount);
}
}
\ No newline at end of file
diff --git a/VpnHood.Server/SystemInformation/ISystemInfoProvider.cs b/VpnHood.Server/SystemInformation/ISystemInfoProvider.cs
index cc6b8ff66..e9ba9a4e8 100644
--- a/VpnHood.Server/SystemInformation/ISystemInfoProvider.cs
+++ b/VpnHood.Server/SystemInformation/ISystemInfoProvider.cs
@@ -3,5 +3,4 @@
public interface ISystemInfoProvider
{
SystemInfo GetSystemInfo();
- string GetOperatingSystemInfo();
}
\ No newline at end of file
diff --git a/VpnHood.Server/SystemInformation/SystemInfo.cs b/VpnHood.Server/SystemInformation/SystemInfo.cs
index bf0f5526a..80d98a251 100644
--- a/VpnHood.Server/SystemInformation/SystemInfo.cs
+++ b/VpnHood.Server/SystemInformation/SystemInfo.cs
@@ -1,13 +1,28 @@
-namespace VpnHood.Server.SystemInformation;
+using VpnHood.Common;
+
+namespace VpnHood.Server.SystemInformation;
public class SystemInfo
{
- public SystemInfo(long totalMemory, long freeMemory)
+ public string OsInfo { get; }
+ public long? TotalMemory { get; set; }
+ public long? AvailableMemory { get; }
+ public int? CpuUsage { get; }
+ public int LogicalCoreCount { get; }
+
+ public SystemInfo(string osInfo, long? totalMemory, long? availableMemory, int? cpuUsage, int logicalCoreCount)
{
+ OsInfo = osInfo;
TotalMemory = totalMemory;
- FreeMemory = freeMemory;
+ AvailableMemory = availableMemory;
+ CpuUsage = cpuUsage;
+ LogicalCoreCount = logicalCoreCount;
+ }
+
+ public override string ToString()
+ {
+ var totalMemory = TotalMemory != null ? Util.FormatBytes(TotalMemory.Value) : "*";
+ return $"{OsInfo}, TotalMemory: {totalMemory}, Logical Cores: {LogicalCoreCount}";
}
- public long TotalMemory { get; }
- public long FreeMemory { get; }
}
\ No newline at end of file
diff --git a/VpnHood.Server/TcpHost.cs b/VpnHood.Server/TcpHost.cs
index 2d6ce1e1b..32f395a39 100644
--- a/VpnHood.Server/TcpHost.cs
+++ b/VpnHood.Server/TcpHost.cs
@@ -12,30 +12,42 @@
using VpnHood.Common.Exceptions;
using VpnHood.Common.Logging;
using VpnHood.Common.Messaging;
+using VpnHood.Server.Exceptions;
using VpnHood.Tunneling;
using VpnHood.Tunneling.Factory;
using VpnHood.Tunneling.Messaging;
namespace VpnHood.Server;
+
internal class TcpHost : IDisposable
{
private const int ServerProtocolVersion = 2;
- private readonly TimeSpan _remoteHostTimeout = TimeSpan.FromSeconds(60);
+ private readonly TimeSpan _requestTimeout = TimeSpan.FromSeconds(60);
private CancellationTokenSource? _cancellationTokenSource;
private readonly SessionManager _sessionManager;
private readonly SocketFactory _socketFactory;
private readonly SslCertificateManager _sslCertificateManager;
private readonly List _tcpListeners = new();
private bool _isIpV6Supported;
- public bool IsDisposed { get; private set; }
private Task? _startTask;
+ private bool _isKeepAliveHasError;
+ private TimeSpan _tcpTimeout;
+
+ public int MaxTcpChannelCount { get; set; } = int.MaxValue;
+ public int MaxTcpConnectWaitCount { get; set; } = int.MaxValue;
+ public TimeSpan TcpConnectTimeout { get; set; } = TimeSpan.FromSeconds(60);
+ public bool IsDisposed { get; private set; }
+
+ public int OrgStreamReadBufferSize { get; set; }
+ public int TunnelStreamReadBufferSize { get; set; }
+ public bool IsStarted { get; private set; }
- private class TlsAuthenticateException : Exception
+ public TimeSpan TcpTimeout
{
- public TlsAuthenticateException(string message, Exception innerException)
- : base(message, innerException)
- {
- }
+ get => _tcpTimeout;
+ set => _tcpTimeout = (value == TimeSpan.Zero || value == Timeout.InfiniteTimeSpan)
+ ? TimeSpan.FromHours(48)
+ : value;
}
public TcpHost(SessionManager sessionManager, SslCertificateManager sslCertificateManager, SocketFactory socketFactory)
@@ -45,11 +57,6 @@ public TcpHost(SessionManager sessionManager, SslCertificateManager sslCertifica
_socketFactory = socketFactory;
}
- public TimeSpan TcpTimeout { get; set; }
- public int OrgStreamReadBufferSize { get; set; }
- public int TunnelStreamReadBufferSize { get; set; }
- public bool IsStarted { get; private set; }
-
public void Start(IPEndPoint[] tcpEndPoints, bool isIpV6Supported)
{
if (IsStarted) throw new Exception($"{nameof(TcpHost)} is already Started!");
@@ -105,17 +112,19 @@ public async Task Stop()
IsStarted = false;
}
- private bool _isKeepAliveSupported = true;
private void EnableKeepAlive(Socket client)
{
+ if (_isKeepAliveHasError)
+ return;
+
try
{
_socketFactory.SetKeepAlive(client, true, TcpTimeout / 2);
}
catch (Exception ex)
{
+ _isKeepAliveHasError = true;
VhLogger.Instance.LogWarning(ex, "KeepAlive is not supported! Consider upgrading your OS.");
- _isKeepAliveSupported = false;
}
}
@@ -129,9 +138,7 @@ private async Task ListenTask(TcpListener tcpListener, CancellationToken cancell
while (!cancellationToken.IsCancellationRequested)
{
var tcpClient = await tcpListener.AcceptTcpClientAsync();
-
- if (TcpTimeout != TimeSpan.Zero && TcpTimeout != Timeout.InfiniteTimeSpan && _isKeepAliveSupported)
- EnableKeepAlive(tcpClient.Client);
+ EnableKeepAlive(tcpClient.Client);
// create cancellation token
_ = ProcessClient(tcpClient, cancellationToken);
@@ -181,7 +188,7 @@ private async Task ProcessClient(TcpClient tcpClient, CancellationToken cancella
using var scope = VhLogger.Instance.BeginScope($"RemoteEp: {VhLogger.Format(tcpClient.Client.RemoteEndPoint)}");
// add timeout to cancellationToken
- using var timeoutCt = new CancellationTokenSource(_remoteHostTimeout);
+ using var timeoutCt = new CancellationTokenSource(_requestTimeout);
using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutCt.Token, cancellationToken);
cancellationToken = cancellationTokenSource.Token;
TcpClientStream? tcpClientStream = null;
@@ -219,7 +226,7 @@ private async Task ProcessClient(TcpClient tcpClient, CancellationToken cancella
await StreamUtil.WriteJsonAsync(tcpClientStream.Stream, new ResponseBase(ex.SessionResponse),
cancellationToken);
- VhLogger.Instance.LogInformation(GeneralEventId.Tcp, ex,
+ VhLogger.Instance.LogInformation(GeneralEventId.Tcp, ex,
$"Connection has been closed. SessionError: {ex.SessionResponse.ErrorCode}.");
}
catch (Exception ex) when (tcpClientStream != null)
@@ -412,34 +419,53 @@ private async Task ProcessTcpProxyChannel(TcpClientStream tcpClientStream, Cance
var request = await StreamUtil.ReadJsonAsync(tcpClientStream.Stream, cancellationToken);
// find session
- using var _ = VhLogger.Instance.BeginScope($"SessionId: {VhLogger.FormatSessionId(request.SessionId)}");
+ using var scope = VhLogger.Instance.BeginScope($"SessionId: {VhLogger.FormatSessionId(request.SessionId)}");
+ var session = await _sessionManager.GetSession(request, tcpClientStream.LocalEndPoint, tcpClientStream.RemoteEndPoint.Address);
+
var isRequestedEpException = false;
+ var isTcpConnectIncreased = false;
TcpClient? tcpClient2 = null;
TcpClientStream? tcpClientStream2 = null;
TcpProxyChannel? tcpProxyChannel = null;
try
{
- var session = await _sessionManager.GetSession(request, tcpClientStream.LocalEndPoint,
- tcpClientStream.RemoteEndPoint.Address);
-
// connect to requested site
VhLogger.Instance.LogTrace(GeneralEventId.StreamChannel,
$"Connecting to the requested endpoint. RequestedEP: {VhLogger.Format(request.DestinationEndPoint)}");
+ // Check tcp wait limit
+ lock (session)
+ {
+ if (session.TcpConnectWaitCount >= MaxTcpConnectWaitCount)
+ throw new MaxTcpConnectException(session.SessionId);
+
+ if (session.TcpChannelCount >= MaxTcpChannelCount)
+ throw new MaxTcpChannelException(session.SessionId);
+
+ Interlocked.Increment(ref session.TcpConnectWaitCount);
+ isTcpConnectIncreased = true;
+ }
+
+ // prepare client
tcpClient2 = _socketFactory.CreateTcpClient(request.DestinationEndPoint.AddressFamily);
- if (TcpTimeout != TimeSpan.Zero && TcpTimeout != Timeout.InfiniteTimeSpan)
- EnableKeepAlive(tcpClient2.Client);
+ EnableKeepAlive(tcpClient2.Client);
//tracking
- session.LogTrack(ProtocolType.Tcp.ToString(), ((IPEndPoint)tcpClient2.Client.LocalEndPoint).Port, request.DestinationEndPoint);
+ session.LogTrack(ProtocolType.Tcp.ToString(),
+ ((IPEndPoint)tcpClient2.Client.LocalEndPoint).Port, request.DestinationEndPoint);
// connect to requested destination
isRequestedEpException = true;
- await Util.RunTask(tcpClient2.ConnectAsync(request.DestinationEndPoint.Address, request.DestinationEndPoint.Port),
- _remoteHostTimeout, cancellationToken);
+ await Util.RunTask(
+ tcpClient2.ConnectAsync(request.DestinationEndPoint.Address, request.DestinationEndPoint.Port),
+ TcpConnectTimeout, cancellationToken);
isRequestedEpException = false;
+ // Release TcpConnectWaitCount
+ Interlocked.Decrement(ref session.TcpConnectWaitCount);
+ isTcpConnectIncreased = false;
+
// send response
await StreamUtil.WriteJsonAsync(tcpClientStream.Stream, session.SessionResponse, cancellationToken);
@@ -449,7 +475,8 @@ await Util.RunTask(tcpClient2.ConnectAsync(request.DestinationEndPoint.Address,
request.CipherKey, null, request.CipherLength);
// add the connection
- VhLogger.Instance.LogTrace(GeneralEventId.StreamChannel, $"Adding a {nameof(TcpProxyChannel)}. SessionId: {VhLogger.FormatSessionId(session.SessionId)}, CipherLength: {request.CipherLength}");
+ VhLogger.Instance.LogTrace(GeneralEventId.StreamChannel,
+ $"Adding a {nameof(TcpProxyChannel)}. SessionId: {VhLogger.FormatSessionId(session.SessionId)}, CipherLength: {request.CipherLength}");
tcpClientStream2 = new TcpClientStream(tcpClient2, tcpClient2.GetStream());
tcpProxyChannel = new TcpProxyChannel(tcpClientStream2, tcpClientStream, TcpTimeout,
@@ -462,11 +489,17 @@ await Util.RunTask(tcpClient2.ConnectAsync(request.DestinationEndPoint.Address,
tcpClient2?.Dispose();
tcpClientStream2?.Dispose();
tcpProxyChannel?.Dispose();
-
+
if (isRequestedEpException)
throw new SessionException(SessionErrorCode.GeneralError, ex.Message);
+
throw;
}
+ finally
+ {
+ if (isTcpConnectIncreased)
+ Interlocked.Decrement(ref session.TcpConnectWaitCount);
+ }
}
public void Dispose()
diff --git a/VpnHood.Server/VpnHood.Server.csproj b/VpnHood.Server/VpnHood.Server.csproj
index 6cfe70859..498f39526 100644
--- a/VpnHood.Server/VpnHood.Server.csproj
+++ b/VpnHood.Server/VpnHood.Server.csproj
@@ -13,9 +13,9 @@
VpnHood.png
The core of VpnHood server. It can listen and accept connections from VpnHood clients.
VpnHood.Server
- 2.6.326
- 2.6.326
- 2.6.326
+ 2.6.328
+ 2.6.328
+ 2.6.328
enable
latest
diff --git a/VpnHood.Server/VpnHoodServer.cs b/VpnHood.Server/VpnHoodServer.cs
index 9d90671a9..7bd5dc124 100644
--- a/VpnHood.Server/VpnHoodServer.cs
+++ b/VpnHood.Server/VpnHoodServer.cs
@@ -32,14 +32,22 @@ public class VpnHoodServer : IDisposable
public VpnHoodServer(IAccessServer accessServer, ServerOptions options)
{
- if (options.SocketFactory == null) throw new ArgumentNullException(nameof(options.SocketFactory));
- _autoDisposeAccessServer = options.AutoDisposeAccessServer;
- _lastConfigFilePath = Path.Combine(options.StoragePath, "last-config.json");
+ if (options.SocketFactory == null)
+ throw new ArgumentNullException(nameof(options.SocketFactory));
+
AccessServer = accessServer;
SystemInfoProvider = options.SystemInfoProvider ?? new BasicSystemInfoProvider();
SessionManager = new SessionManager(accessServer, options.SocketFactory, options.Tracker);
- _tcpHost = new TcpHost(SessionManager, new SslCertificateManager(AccessServer), options.SocketFactory);
+
+ _autoDisposeAccessServer = options.AutoDisposeAccessServer;
+ _lastConfigFilePath = Path.Combine(options.StoragePath, "last-config.json");
_publicIpDiscovery = options.PublicIpDiscovery;
+ _tcpHost = new TcpHost(SessionManager, new SslCertificateManager(AccessServer), options.SocketFactory)
+ {
+ TcpConnectTimeout = options.TcpConnectTimeout,
+ MaxTcpConnectWaitCount = options.MaxTcpConnectWaitCount,
+ MaxTcpChannelCount = options.MaxTcpChannelCount
+ };
// Configure thread pool size
ThreadPool.GetMinThreads(out var workerThreads, out var completionPortThreads);
@@ -109,7 +117,7 @@ public async Task Start()
// Report current OS Version
VhLogger.Instance.LogInformation($"{GetType().Assembly.GetName().FullName}");
- VhLogger.Instance.LogInformation($"OS: {SystemInfoProvider.GetOperatingSystemInfo()}");
+ VhLogger.Instance.LogInformation($"OS: {SystemInfoProvider.GetSystemInfo()}");
// report config
ThreadPool.GetMinThreads(out var minWorkerThreads, out var minCompletionPortThreads);
@@ -137,6 +145,7 @@ private async Task Configure()
// get server info
VhLogger.Instance.LogInformation("Configuring by the Access Server...");
+ var providerSystemInfo = SystemInfoProvider.GetSystemInfo();
var serverInfo = new ServerInfo(
environmentVersion: Environment.Version,
version: typeof(VpnHoodServer).Assembly.GetName().Version,
@@ -146,9 +155,10 @@ private async Task Configure()
)
{
MachineName = Environment.MachineName,
- OsInfo = SystemInfoProvider.GetOperatingSystemInfo(),
+ OsInfo = providerSystemInfo.OsInfo,
OsVersion = Environment.OSVersion.ToString(),
- TotalMemory = SystemInfoProvider.GetSystemInfo().TotalMemory,
+ TotalMemory = providerSystemInfo.TotalMemory,
+ LogicalCoreCount = providerSystemInfo.LogicalCoreCount,
LastError = _lastConfigError
};
var isIpv6Supported = serverInfo.PublicIpAddresses.Any(x => x.AddressFamily == AddressFamily.InterNetworkV6);
@@ -198,8 +208,11 @@ private async Task Configure()
}
}
- private static int GetBestTcpBufferSize(long totalMemory)
+ private static int GetBestTcpBufferSize(long? totalMemory)
{
+ if (totalMemory == null)
+ return 8192;
+
var bufferSize = (long)Math.Round((double)totalMemory / 0x80000000) * 4096;
bufferSize = Math.Max(bufferSize, 8192);
bufferSize = Math.Min(bufferSize, 8192); //81920, it looks it doesn't have effect
@@ -244,10 +257,11 @@ public ServerStatus Status
var serverStatus = new ServerStatus
{
SessionCount = SessionManager.Sessions.Count(x => !x.Value.IsDisposed),
- TcpConnectionCount = SessionManager.Sessions.Values.Sum(x => x.TcpConnectionCount),
+ TcpConnectionCount = SessionManager.Sessions.Values.Sum(x => x.TcpChannelCount),
UdpConnectionCount = SessionManager.Sessions.Values.Sum(x => x.UdpConnectionCount),
ThreadCount = Process.GetCurrentProcess().Threads.Count,
- FreeMemory = systemInfo.FreeMemory,
+ AvailableMemory = systemInfo.AvailableMemory,
+ CpuUsage = systemInfo.CpuUsage,
UsedMemory = Process.GetCurrentProcess().WorkingSet64,
TunnelSendSpeed = SessionManager.Sessions.Sum(x => x.Value.Tunnel.SendSpeed),
TunnelReceiveSpeed = SessionManager.Sessions.Sum(x => x.Value.Tunnel.ReceiveSpeed),
diff --git a/VpnHood.Tunneling/Tunnel.cs b/VpnHood.Tunneling/Tunnel.cs
index cb87b5497..e53167324 100644
--- a/VpnHood.Tunneling/Tunnel.cs
+++ b/VpnHood.Tunneling/Tunnel.cs
@@ -12,7 +12,6 @@ namespace VpnHood.Tunneling;
public class Tunnel : IDisposable
{
- private const int SpeedThreshold = 2;
private readonly object _channelListLock = new();
private readonly int _maxQueueLength = 100;
private readonly int _mtuNoFragment = TunnelUtil.MtuWithoutFragmentation;
@@ -20,9 +19,6 @@ public class Tunnel : IDisposable
private readonly Queue _packetQueue = new();
private readonly SemaphoreSlim _packetSentEvent = new(0);
private readonly SemaphoreSlim _packetSenderSemaphore = new(0);
- private readonly Queue _receivedBytes = new();
- private readonly Queue _sentBytes = new();
- private readonly object _speedMonitorLock = new();
private readonly HashSet _streamChannels = new();
private readonly Timer _speedMonitorTimer;
private bool _disposed;
@@ -33,14 +29,20 @@ public class Tunnel : IDisposable
private long _sentByteCount;
private readonly TimeSpan _datagramPacketTimeout = TimeSpan.FromSeconds(100);
private readonly TimeSpan _tcpTimeout;
- private DateTime _lastTcpProxyCleanup = DateTime.Now;
+ private DateTime _lastTcpProxyCleanupTime = DateTime.Now;
+ private DateTime _lastSpeedUpdateTime = DateTime.Now;
+ private readonly TimeSpan _speedTestThreshold = TimeSpan.FromSeconds(2);
+
+ public long SendSpeed { get; private set; }
+ public long ReceiveSpeed { get; private set; }
+ public DateTime LastActivityTime { get; private set; } = DateTime.Now;
public Tunnel(TunnelOptions? options = null)
{
options ??= new TunnelOptions();
_tcpTimeout = options.TcpTimeout;
_maxDatagramChannelCount = options.MaxDatagramChannelCount;
- _speedMonitorTimer = new Timer(SpeedMonitor, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
+ _speedMonitorTimer = new Timer(_ => UpdateSpeed(), null, TimeSpan.Zero, _speedTestThreshold);
}
public int StreamChannelCount => _streamChannels.Count;
@@ -72,7 +74,7 @@ public long SentByteCount
public void Cleanup()
{
- if (_lastTcpProxyCleanup + _tcpTimeout / 2 < DateTime.Now)
+ if (_lastTcpProxyCleanupTime + _tcpTimeout / 2 < DateTime.Now)
{
var channels = Util.SafeToArray(_streamChannels, _streamChannels);
foreach (var item in channels)
@@ -80,14 +82,10 @@ public void Cleanup()
if (item is TcpProxyChannel tcpProxyChannel)
tcpProxyChannel.CheckConnection();
}
- _lastTcpProxyCleanup = DateTime.Now;
+ _lastTcpProxyCleanupTime = DateTime.Now;
}
}
- public long SendSpeed { get; private set; }
- public long ReceiveSpeed { get; private set; }
- public DateTime LastActivityTime { get; private set; } = DateTime.Now;
-
public int MaxDatagramChannelCount
{
get => _maxDatagramChannelCount;
@@ -102,40 +100,28 @@ public int MaxDatagramChannelCount
public event EventHandler? OnPacketReceived;
public event EventHandler? OnChannelAdded;
public event EventHandler? OnChannelRemoved;
- public event EventHandler? OnTrafficChanged;
- private void SpeedMonitor(object state)
+ private void UpdateSpeed()
{
- if (_disposed) return;
-
- bool trafficChanged;
- lock (_speedMonitorLock)
- {
- var sentByteCount = SentByteCount;
- var receivedByteCount = ReceivedByteCount;
- trafficChanged = _lastSentByteCount != sentByteCount || _lastReceivedByteCount != receivedByteCount;
-
- // add transferred bytes
- _sentBytes.Enqueue(sentByteCount - _lastSentByteCount);
- _receivedBytes.Enqueue(receivedByteCount - _lastReceivedByteCount);
- if (_sentBytes.Count > SpeedThreshold) _sentBytes.TryDequeue(out _);
- if (_receivedBytes.Count > SpeedThreshold) _receivedBytes.TryDequeue(out _);
-
- // calculate speed
- SendSpeed = _sentBytes.Sum() / SpeedThreshold;
- ReceiveSpeed = _receivedBytes.Sum() / SpeedThreshold;
-
- // save last traffic
- _lastSentByteCount = sentByteCount;
- _lastReceivedByteCount = receivedByteCount;
- }
+ if (_disposed)
+ return;
+
+ if (DateTime.Now - _lastSpeedUpdateTime < _speedTestThreshold)
+ return;
- // fire traffic changed
+ var sentByteCount = SentByteCount;
+ var receivedByteCount = ReceivedByteCount;
+ var trafficChanged = _lastSentByteCount != sentByteCount || _lastReceivedByteCount != receivedByteCount;
+ var duration = (DateTime.Now - _lastSpeedUpdateTime).TotalSeconds;
+
+ SendSpeed = (int)((sentByteCount - _lastSentByteCount) / duration);
+ ReceiveSpeed = (int)((receivedByteCount - _lastReceivedByteCount) / duration);
+
+ _lastSpeedUpdateTime = DateTime.Now;
+ _lastSentByteCount = sentByteCount;
+ _lastReceivedByteCount = receivedByteCount;
if (trafficChanged)
- {
LastActivityTime = DateTime.Now;
- OnTrafficChanged?.Invoke(this, EventArgs.Empty);
- }
}
private bool IsChannelExists(IChannel channel)
@@ -423,6 +409,8 @@ public void Dispose()
_packetQueue.Clear();
_speedMonitorTimer.Dispose();
+ SendSpeed = 0;
+ ReceiveSpeed = 0;
// release worker threads
_packetSenderSemaphore.Release(MaxDatagramChannelCount * 10); //make sure to release all semaphores
diff --git a/VpnHood.Tunneling/VpnHood.Tunneling.csproj b/VpnHood.Tunneling/VpnHood.Tunneling.csproj
index 4e9ec1364..2598b8598 100644
--- a/VpnHood.Tunneling/VpnHood.Tunneling.csproj
+++ b/VpnHood.Tunneling/VpnHood.Tunneling.csproj
@@ -15,9 +15,9 @@
VpnHood.png
VpnHood.Tunneling
Provides tunnelling classes and protocols shared between VpnHood.Client and VpnHood.Server.
- 2.6.326
- 2.6.326
- 2.6.326
+ 2.6.328
+ 2.6.328
+ 2.6.328
enable
latest
diff --git a/VpnHood.ZTest/TestHelper.cs b/VpnHood.ZTest/TestHelper.cs
index 47b5db28f..d55ec5ada 100644
--- a/VpnHood.ZTest/TestHelper.cs
+++ b/VpnHood.ZTest/TestHelper.cs
@@ -140,9 +140,9 @@ public static void Test_Udp(UdpClient? udpClient = null, IPEndPoint? ntpEndPoint
Assert.IsTrue(time > DateTime.Now.AddMinutes(-10));
}
- public static void Test_Https(HttpClient? httpClient = default, Uri? uri = default, int timeout = 3000)
+ public static void Test_Https(HttpClient? httpClient = default, Uri? uri = default, int timeout = 3000, bool throwError = true)
{
- if (!SendHttpGet(httpClient, uri, timeout))
+ if (!SendHttpGet(httpClient, uri, timeout) && throwError)
throw new Exception("Https get doesn't work!");
}
@@ -157,6 +157,7 @@ public static IPAddress[] GetTestIpAddresses()
addresses.Add(TEST_NtpEndPoint2.Address);
addresses.Add(TEST_PingAddress1);
addresses.Add(TEST_PingAddress2);
+ addresses.Add(TEST_InvalidIp);
addresses.Add(new ClientOptions().TcpProxyLoopbackAddressIpV4);
return addresses.ToArray();
}
@@ -195,8 +196,9 @@ public static FileAccessServerOptions CreateFileAccessServerOptions()
TcpEndPoints = new[] { Util.GetFreeEndPoint(IPAddress.Loopback) },
SessionOptions =
{
- SyncCacheSize = 50
- }
+ SyncCacheSize = 50,
+ IcmpTimeout = TimeSpan.FromMilliseconds(100)
+ },
};
return options;
}
diff --git a/VpnHood.ZTest/Tests/AccessTest.cs b/VpnHood.ZTest/Tests/AccessTest.cs
index 309b26c5c..2f8c30226 100644
--- a/VpnHood.ZTest/Tests/AccessTest.cs
+++ b/VpnHood.ZTest/Tests/AccessTest.cs
@@ -1,8 +1,10 @@
using System;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VpnHood.Client;
+using VpnHood.Common.Logging;
using VpnHood.Common.Messaging;
namespace VpnHood.Test.Tests;
@@ -10,6 +12,12 @@ namespace VpnHood.Test.Tests;
[TestClass]
public class AccessTest
{
+ [TestInitialize]
+ public void Initialize()
+ {
+ VhLogger.Instance = VhLogger.CreateConsoleLogger(true);
+ }
+
[TestMethod]
public void Server_reject_invalid_requests()
{
diff --git a/VpnHood.ZTest/Tests/ClientServerTest.cs b/VpnHood.ZTest/Tests/ClientServerTest.cs
index eb10ff3be..97d0f61e7 100644
--- a/VpnHood.ZTest/Tests/ClientServerTest.cs
+++ b/VpnHood.ZTest/Tests/ClientServerTest.cs
@@ -15,6 +15,7 @@
using VpnHood.Common.Messaging;
using VpnHood.Server;
using VpnHood.Server.Providers.FileAccessServerProvider;
+using VpnHood.Test.Factory;
namespace VpnHood.Test.Tests;
@@ -253,7 +254,6 @@ public void Client_must_dispose_after_device_closed()
Assert.AreEqual(ClientState.Disposed, client.State);
}
-
[TestMethod]
public void Client_must_dispose_after_server_stopped()
{
@@ -506,7 +506,7 @@ public void AutoReconnect()
TestHelper.Test_Https(); //let transfer something
- fileAccessServer.SessionManager.Sessions.TryRemove(clientConnect.Client.SessionId, out _ );
+ fileAccessServer.SessionManager.Sessions.TryRemove(clientConnect.Client.SessionId, out _);
server.SessionManager.Sessions.TryRemove(clientConnect.Client.SessionId, out _);
try
@@ -584,5 +584,79 @@ public void Disconnect_for_unsupported_client()
Assert.AreEqual(SessionErrorCode.UnsupportedClient, client.SessionStatus.ErrorCode);
}
}
+
+ [TestMethod]
+ public async Task Server_limit_by_Max_TcpConnectWait()
+ {
+ // create access server
+ using var fileAccessServer = TestHelper.CreateFileAccessServer();
+ using var testAccessServer = new TestAccessServer(fileAccessServer);
+
+ // ser server options
+ var serverOptions = new ServerOptions
+ {
+ SocketFactory = new TestSocketFactory(true),
+ StoragePath = TestHelper.WorkingPath,
+ PublicIpDiscovery = false, //it slows down the running tests
+ MaxTcpConnectWaitCount = 2
+ };
+ using var server = new VpnHoodServer(testAccessServer, serverOptions);
+ await server.Start();
+
+ // create client
+ var token = TestHelper.CreateAccessToken(server);
+ using var client = TestHelper.CreateClient(token);
+
+ using var httpClient = new HttpClient();
+ _ = httpClient.GetStringAsync($"https://{TestHelper.TEST_InvalidIp}:4441");
+ _ = httpClient.GetStringAsync($"https://{TestHelper.TEST_InvalidIp}:4442");
+ _ = httpClient.GetStringAsync($"https://{TestHelper.TEST_InvalidIp}:4443");
+ _ = httpClient.GetStringAsync($"https://{TestHelper.TEST_InvalidIp}:4445");
+
+ await Task.Delay(1000);
+ var session = server.SessionManager.GetSessionById(client.SessionId);
+ Assert.AreEqual(serverOptions.MaxTcpConnectWaitCount, session?.TcpConnectWaitCount);
+ }
+
+ [TestMethod]
+ public async Task Server_limit_by_Max_TcpChannel()
+ {
+ // create access server
+ using var fileAccessServer = TestHelper.CreateFileAccessServer();
+ using var testAccessServer = new TestAccessServer(fileAccessServer);
+
+ // ser server options
+ var serverOptions = new ServerOptions
+ {
+ SocketFactory = new TestSocketFactory(true),
+ StoragePath = TestHelper.WorkingPath,
+ PublicIpDiscovery = false, //it slows down the running tests
+ MaxTcpChannelCount = 2
+ };
+ using var server = new VpnHoodServer(testAccessServer, serverOptions);
+ await server.Start();
+
+ // create client
+ var token = TestHelper.CreateAccessToken(server);
+ using var client = TestHelper.CreateClient(token);
+
+ using var tcpClient1 = new TcpClient();
+ using var tcpClient2 = new TcpClient();
+ using var tcpClient3 = new TcpClient();
+ using var tcpClient4 = new TcpClient();
+
+ await tcpClient1.ConnectAsync(TestHelper.TEST_HttpsUri1.Host, 443);
+ await Task.Delay(250);
+ await tcpClient2.ConnectAsync(TestHelper.TEST_HttpsUri1.Host, 443);
+ await Task.Delay(250);
+ await tcpClient3.ConnectAsync(TestHelper.TEST_HttpsUri2.Host, 443);
+ await Task.Delay(250);
+ await tcpClient4.ConnectAsync(TestHelper.TEST_HttpsUri2.Host, 443);
+ await Task.Delay(250);
+
+ var session = server.SessionManager.GetSessionById(client.SessionId);
+ Assert.AreEqual(serverOptions.MaxTcpChannelCount, session?.TcpChannelCount);
+ }
+
#endif
}
\ No newline at end of file
diff --git a/VpnHood.ZTest/VpnHood.ZTest.csproj b/VpnHood.ZTest/VpnHood.ZTest.csproj
index 4e0e580f8..2f6049cd7 100644
--- a/VpnHood.ZTest/VpnHood.ZTest.csproj
+++ b/VpnHood.ZTest/VpnHood.ZTest.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/VpnHood.sln.DotSettings b/VpnHood.sln.DotSettings
index ed88892b1..f02685df3 100644
--- a/VpnHood.sln.DotSettings
+++ b/VpnHood.sln.DotSettings
@@ -15,13 +15,16 @@
True
True
True
+ True
True
True
True
+ True
True
True
True
True
+ True
True
True
True