Compare commits

...

18 Commits
1.8.6 ... 1.8.7

Author SHA1 Message Date
ChsBuffer
e1f3390787 Bump version to 1.8.7 2021-07-16 05:25:36 +08:00
ChsBuffer
a62f694908 Refactors 2021-07-16 05:24:36 +08:00
ChsBuffer
98f3218e28 Fixup Remove unused Control.Invoke 2021-07-16 03:46:57 +08:00
ChsBuffer
9da801dc19 Update Log
Remove unused Control.Invoke
2021-07-16 03:39:16 +08:00
ChsBuffer
ff7ae73156 Update using FileStream 2021-07-16 03:25:35 +08:00
ChsBuffer
6810bcc87f Update UnitTest.csproj dependencies 2021-07-16 02:21:17 +08:00
ChsBuffer
3462a3badf add global.json sdk.allowPrerelease=false 2021-07-16 02:20:10 +08:00
ChsBuffer
87a3581dff Fix Configuration.Save() Close FileStream 2021-07-16 00:29:21 +08:00
ChsBuffer
060c42efbc Import Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD 2021-07-15 22:45:05 +08:00
ChsBuffer
72ae9b7bf3 Make Task Analyzer Happy 2021-07-15 20:15:34 +08:00
ChsBuffer
bf4b637940 Add Microsoft.VisualStudio.Threading 2021-07-15 17:57:43 +08:00
ChsBuffer
f8b1c4acd6 Fix Disable edit server if the server is in a group 2021-07-15 16:06:34 +08:00
ChsBuffer
88f3a4940b Add UnitTest 2021-07-15 15:14:40 +08:00
ChsBuffer
cb8dc2163f Update Solution To Include Additional Files 2021-07-15 14:30:46 +08:00
ChsBuffer
ee206f3df0 Update Log 2021-07-15 14:29:56 +08:00
ChsBuffer
d5e1ef1a56 Migrate to CsWin32 2021-07-15 01:54:52 +08:00
ChsBuffer
aeaef4e125 CI Disable download.ps1 2021-07-15 01:54:29 +08:00
ChsBuffer
352602a7ed Fix Confiugration.Save not touch replace target file 2021-07-15 00:55:39 +08:00
48 changed files with 578 additions and 387 deletions

View File

@@ -1,10 +1,21 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29009.5
# Visual Studio Version 17
VisualStudioVersion = 17.0.31423.177
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Netch", "Netch\Netch.csproj", "{4B041B91-5790-4571-8C58-C63FFE4BC9F8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "UnitTest\UnitTest.csproj", "{38240783-9AD2-4A01-84C1-1A3E5F05720F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AdditionalFiles", "AdditionalFiles", "{B7354F81-F79C-4C23-9067-C4DAE91B56F0}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitignore = .gitignore
LICENSE = LICENSE
README.md = README.md
global.json = global.json
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -15,6 +26,10 @@ Global
{4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Debug|x64.Build.0 = Debug|x64
{4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Release|x64.ActiveCfg = Release|x64
{4B041B91-5790-4571-8C58-C63FFE4BC9F8}.Release|x64.Build.0 = Release|x64
{38240783-9AD2-4A01-84C1-1A3E5F05720F}.Debug|x64.ActiveCfg = Debug|Any CPU
{38240783-9AD2-4A01-84C1-1A3E5F05720F}.Debug|x64.Build.0 = Debug|Any CPU
{38240783-9AD2-4A01-84C1-1A3E5F05720F}.Release|x64.ActiveCfg = Release|Any CPU
{38240783-9AD2-4A01-84C1-1A3E5F05720F}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -14,6 +14,8 @@
public const string OutputTemplate = @"[{Timestamp:yyyy-MM-dd HH:mm:ss}][{Level}] {Message:lj}{NewLine}{Exception}";
public const string EOF = "\r\n";
public const string DefaultGroup = "NONE";
public static class Parameter
{
public const string Show = "-show";

View File

@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Netch.Interfaces;
using static Netch.Interops.AioDNS;
@@ -9,12 +10,12 @@ namespace Netch.Controllers
{
public string Name => "DNS Service";
public void Stop()
public async Task StopAsync()
{
Free();
await FreeAsync();
}
public void Start()
public async Task StartAsync()
{
MainController.PortCheck(Global.Settings.AioDNS.ListenPort, "DNS");
@@ -27,7 +28,7 @@ namespace Netch.Controllers
Dial(NameList.TYPE_CDNS, $"{aioDnsConfig.ChinaDNS}");
Dial(NameList.TYPE_ODNS, $"{aioDnsConfig.OtherDNS}");
if (!Init())
if (!await InitAsync())
throw new Exception("AioDNS start failed.");
}
}

View File

@@ -4,8 +4,8 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Threading;
using Netch.Models;
using Netch.Utils;
using Serilog;
@@ -67,11 +67,11 @@ namespace Netch.Controllers
Instance.Dispose();
}
protected void StartGuard(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal)
protected async Task StartGuardAsync(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal)
{
State = State.Starting;
_logFileStream = File.Open(LogPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
_logFileStream = File.Open(LogPath, FileMode.Create, FileAccess.Write, FileShare.Read);
_logStreamWriter = new StreamWriter(_logFileStream) { AutoFlush = true };
Instance.StartInfo.Arguments = argument;
@@ -82,8 +82,8 @@ namespace Netch.Controllers
if (RedirectOutput)
{
Task.Run(() => ReadOutput(Instance.StandardOutput));
Task.Run(() => ReadOutput(Instance.StandardError));
Task.Run(() => ReadOutput(Instance.StandardOutput)).Forget();
Task.Run(() => ReadOutput(Instance.StandardError)).Forget();
if (!StartedKeywords.Any())
{
@@ -95,20 +95,20 @@ namespace Netch.Controllers
// wait ReadOutput change State
for (var i = 0; i < 1000; i++)
{
Thread.Sleep(10);
await Task.Delay(50);
switch (State)
{
case State.Started:
OnStarted();
return;
case State.Stopped:
StopGuard();
await StopGuardAsync();
OnStartFailed();
throw new MessageException($"{Name} 控制器启动失败");
}
}
StopGuard();
await StopGuardAsync();
throw new MessageException($"{Name} 控制器启动超时");
}
}
@@ -136,12 +136,12 @@ namespace Netch.Controllers
State = State.Stopped;
}
public virtual void Stop()
public virtual async Task StopAsync()
{
StopGuard();
await StopGuardAsync();
}
protected void StopGuard()
protected async Task StopGuardAsync()
{
_logStreamWriter?.Close();
_logFileStream?.Close();
@@ -151,7 +151,7 @@ namespace Netch.Controllers
if (Instance is { HasExited: false })
{
Instance.Kill();
Instance.WaitForExit();
await Instance.WaitForExitAsync();
}
}
catch (Win32Exception e)

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Threading;
using Netch.Interfaces;
using Netch.Models;
using Netch.Utils;
@@ -21,9 +22,9 @@ namespace Netch.Controllers
public static async Task StartAsync(Server server, Mode mode)
{
Log.Information("启动主控制器: {Server} {Mode}", $"{server.Type}", $"[{(int) mode.Type}]{mode.Remark}");
Log.Information("Start MainController: {Server} {Mode}", $"{server.Type}", $"[{(int)mode.Type}]{mode.Remark}");
if (DnsUtils.Lookup(server.Hostname) == null)
if (await DnsUtils.LookupAsync(server.Hostname) == null)
throw new MessageException(i18N.Translate("Lookup Server hostname failed"));
Mode = mode;
@@ -35,17 +36,18 @@ namespace Netch.Controllers
if (Log.IsEnabled(LogEventLevel.Debug))
Task.Run(() =>
{
// TODO log level setting
Log.Debug("Running Processes: \n{Processes}", string.Join("\n", SystemInfo.Processes(false)));
}).Forget();
{
// TODO log level setting
Log.Debug("Running Processes: \n{Processes}", string.Join("\n", SystemInfo.Processes(false)));
})
.Forget();
try
{
if (!ModeHelper.SkipServerController(server, mode))
server = await Task.Run(() => StartServer(server));
server = await StartServerAsync(server);
await Task.Run(() => StartMode(server, mode));
await StartModeAsync(server, mode);
}
catch (Exception e)
{
@@ -59,13 +61,14 @@ namespace Netch.Controllers
case MessageException:
throw;
default:
Log.Error(e, "主控制器启动未处理异常");
throw new MessageException($"未处理异常\n{e.Message}");
Log.Error(e, "Unhandled Exception When Start MainController");
Utils.Utils.Open(Constants.LogFile);
throw new MessageException($"{i18N.Translate("Unhandled Exception")}\n{e.Message}");
}
}
}
private static Server StartServer(Server server)
private static async Task<Server> StartServerAsync(Server server)
{
ServerController = ServerHelper.GetUtilByTypeName(server.Type).GetController();
@@ -73,8 +76,8 @@ namespace Netch.Controllers
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ServerController.Name));
Log.Debug($"{server.Type} {server.MaskedData()}");
var socks5 = ServerController.Start(server);
Log.Debug("Server Information: {Data}", $"{server.Type} {server.MaskedData()}");
var socks5 = await ServerController.StartAsync(server);
StatusPortInfoText.Socks5Port = socks5.Port;
StatusPortInfoText.UpdateShareLan();
@@ -82,16 +85,16 @@ namespace Netch.Controllers
return socks5;
}
private static void StartMode(Server server, Mode mode)
private static async Task StartModeAsync(Server server, Mode mode)
{
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName);
if (port != null)
TryReleaseTcpPort((ushort) port, portName);
TryReleaseTcpPort((ushort)port, portName);
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
ModeController.Start(server, mode);
await ModeController.StartAsync(server, mode);
}
public static async Task StopAsync()
@@ -99,14 +102,15 @@ namespace Netch.Controllers
if (ServerController == null && ModeController == null)
return;
Log.Information("Stop Main Controller");
StatusPortInfoText.Reset();
Task.Run(() => NTTController.Stop()).Forget();
Task.Run(() => NTTController.StopAsync()).Forget();
var tasks = new[]
{
Task.Run(() => ServerController?.Stop()),
Task.Run(() => ModeController?.Stop())
Task.Run(() => ServerController?.StopAsync()),
Task.Run(() => ModeController?.StopAsync())
};
try
@@ -115,7 +119,7 @@ namespace Netch.Controllers
}
catch (Exception e)
{
Log.Error(e, "主控制器停止未处理异常");
Log.Error(e, "MainController Stop Error");
}
ModeController = null;

View File

@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Threading.Tasks;
using Netch.Interfaces;
using Netch.Interops;
using Netch.Models;
@@ -27,7 +28,7 @@ namespace Netch.Controllers
public string Name => "Redirector";
public void Start(Server server, Mode mode)
public async Task StartAsync(Server server, Mode mode)
{
_server = server;
_mode = mode;
@@ -43,7 +44,7 @@ namespace Netch.Controllers
// Server
Dial(NameList.TYPE_FILTERUDP, _rdrConfig.FilterProtocol.HasFlag(PortType.UDP).ToString().ToLower());
Dial(NameList.TYPE_FILTERTCP, _rdrConfig.FilterProtocol.HasFlag(PortType.TCP).ToString().ToLower());
dial_Server(_rdrConfig.FilterProtocol, _server);
await DialServerAsync(_rdrConfig.FilterProtocol, _server);
// Mode Rule
dial_Name(_mode);
@@ -51,13 +52,13 @@ namespace Netch.Controllers
// Features
Dial(NameList.TYPE_DNSHOST, _rdrConfig.DNSHijack ? _rdrConfig.DNSHijackHost : "");
if (!Init())
if (!await InitAsync())
throw new MessageException("Redirector start failed.");
}
public void Stop()
public async Task StopAsync()
{
Free();
await FreeAsync();
}
#region CheckRule
@@ -102,12 +103,12 @@ namespace Netch.Controllers
#endregion
private void dial_Server(PortType portType, in Server server)
private async Task DialServerAsync(PortType portType, Server server)
{
if (portType == PortType.Both)
{
dial_Server(PortType.TCP, server);
dial_Server(PortType.UDP, server);
await DialServerAsync(PortType.TCP, server);
await DialServerAsync(PortType.UDP, server);
return;
}
@@ -116,7 +117,7 @@ namespace Netch.Controllers
if (server is Socks5 socks5)
{
Dial(NameList.TYPE_TCPTYPE + offset, "Socks5");
Dial(NameList.TYPE_TCPHOST + offset, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
Dial(NameList.TYPE_TCPHOST + offset, $"{await socks5.AutoResolveHostnameAsync()}:{socks5.Port}");
Dial(NameList.TYPE_TCPUSER + offset, socks5.Username ?? string.Empty);
Dial(NameList.TYPE_TCPPASS + offset, socks5.Password ?? string.Empty);
Dial(NameList.TYPE_TCPMETH + offset, string.Empty);
@@ -124,7 +125,7 @@ namespace Netch.Controllers
else if (server is Shadowsocks shadowsocks && !shadowsocks.HasPlugin() && _rdrConfig.RedirectorSS)
{
Dial(NameList.TYPE_TCPTYPE + offset, "Shadowsocks");
Dial(NameList.TYPE_TCPHOST + offset, $"{shadowsocks.AutoResolveHostname()}:{shadowsocks.Port}");
Dial(NameList.TYPE_TCPHOST + offset, $"{await shadowsocks.AutoResolveHostnameAsync()}:{shadowsocks.Port}");
Dial(NameList.TYPE_TCPMETH + offset, shadowsocks.EncryptMethod);
Dial(NameList.TYPE_TCPPASS + offset, shadowsocks.Password);
}

View File

@@ -20,7 +20,7 @@ namespace Netch.Controllers
/// 启动 NatTypeTester
/// </summary>
/// <returns></returns>
public async Task<(string? result, string? localEnd, string? publicEnd)> Start()
public async Task<(string? result, string? localEnd, string? publicEnd)> StartAsync()
{
string? localEnd = null, publicEnd = null, result = null, bindingTest = null;
@@ -42,12 +42,17 @@ namespace Netch.Controllers
}
if (output.IsNullOrWhiteSpace())
if (!error.IsNullOrWhiteSpace())
{
if (error.IsNullOrWhiteSpace())
{
var errorFirst = error.GetLines().First();
return (errorFirst.SplitTrimEntries(':').Last(), null, null);
Log.Warning("NTT no output");
return (null, null, null);
}
var errorFirst = error.GetLines().First();
return (errorFirst.SplitTrimEntries(':').Last(), null, null);
}
foreach (var line in output.Split('\n'))
{
var str = line.SplitTrimEntries(':');
@@ -87,7 +92,7 @@ namespace Netch.Controllers
Log.Error(e, "{Name} 控制器启动异常", Name);
try
{
Stop();
await StopAsync();
}
catch
{

View File

@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Threading;
using Netch.Forms;
using Netch.Interfaces;
using Netch.Models;
@@ -29,7 +31,7 @@ namespace Netch.Controllers
public override string Name => "pcap2socks";
public void Start(Server server, Mode mode)
public async Task StartAsync(Server server, Mode mode)
{
_server = server;
_mode = mode;
@@ -37,19 +39,19 @@ namespace Netch.Controllers
var outboundNetworkInterface = NetworkInterfaceUtils.GetBest();
var argument = new StringBuilder($@"-i \Device\NPF_{outboundNetworkInterface.Id}");
if (_server is Socks5 socks5 && !socks5.Auth())
argument.Append($" --destination {socks5.AutoResolveHostname()}:{socks5.Port}");
if (_server is Socks5Bridge socks5)
argument.Append($" --destination {await socks5.AutoResolveHostnameAsync()}:{socks5.Port}");
else
argument.Append($" --destination 127.0.0.1:{Global.Settings.Socks5LocalPort}");
Trace.Assert(false);
argument.Append($" {_mode.GetRules().FirstOrDefault() ?? "-P n"}");
StartGuard(argument.ToString());
await StartGuardAsync(argument.ToString());
}
public override void Stop()
public override async Task StopAsync()
{
Global.MainForm.Invoke(new Action(() => { _form.Close(); }));
StopGuard();
await StopGuardAsync();
}
~PcapController()
@@ -76,10 +78,11 @@ namespace Netch.Controllers
if (new FileInfo(LogPath).Length == 0)
{
Task.Run(() =>
{
Thread.Sleep(1000);
Utils.Utils.Open("https://github.com/zhxie/pcap2socks#dependencies");
});
{
Thread.Sleep(1000);
Utils.Utils.Open("https://github.com/zhxie/pcap2socks#dependencies");
})
.Forget();
throw new MessageException("Pleases install pcap2socks's dependency");
}

View File

@@ -30,13 +30,13 @@ namespace Netch.Controllers
public string Name => "tun2socks";
public void Start(Server server, Mode mode)
public async Task StartAsync(Server server, Mode mode)
{
_mode = mode;
_tunConfig = Global.Settings.TUNTAP;
if (server is Socks5Bridge socks5Bridge)
_serverRemoteAddress = DnsUtils.Lookup(socks5Bridge.RemoteHostname);
_serverRemoteAddress = await DnsUtils.LookupAsync(socks5Bridge.RemoteHostname);
if (_serverRemoteAddress != null && IPAddress.IsLoopback(_serverRemoteAddress))
_serverRemoteAddress = null;
@@ -58,9 +58,9 @@ namespace Netch.Controllers
if (server is Socks5 socks5)
{
Dial(NameList.TYPE_TCPHOST, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
Dial(NameList.TYPE_TCPHOST, $"{await socks5.AutoResolveHostnameAsync()}:{socks5.Port}");
Dial(NameList.TYPE_UDPHOST, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
Dial(NameList.TYPE_UDPHOST, $"{await socks5.AutoResolveHostnameAsync()}:{socks5.Port}");
if (socks5.Auth())
{
@@ -86,7 +86,7 @@ namespace Netch.Controllers
}
else
{
_aioDnsController.Start();
await _aioDnsController.StartAsync();
Dial(NameList.TYPE_DNSADDR, $"127.0.0.1:{Global.Settings.AioDNS.ListenPort}");
}
@@ -106,16 +106,16 @@ namespace Netch.Controllers
SetupRouteTable();
}
public void Stop()
public async Task StopAsync()
{
var tasks = new[]
{
Task.Run(Free),
FreeAsync(),
Task.Run(ClearRouteTable),
Task.Run(_aioDnsController.Stop)
_aioDnsController.StopAsync()
};
Task.WaitAll(tasks);
await Task.WhenAll(tasks);
}
private void CheckDriver()
@@ -147,7 +147,6 @@ namespace Netch.Controllers
private void SetupRouteTable()
{
Global.MainForm.StatusText(i18N.Translate("Setup Route Table Rule"));
Log.Information("设置路由规则");
// Server Address
if (_serverRemoteAddress != null)

View File

@@ -20,7 +20,7 @@ namespace Netch.Controllers
public const string Name = @"Netch";
public const string Copyright = @"Copyright © 2019 - 2021";
public const string AssemblyVersion = @"1.8.6";
public const string AssemblyVersion = @"1.8.7";
private const string Suffix = @"";
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
@@ -37,14 +37,14 @@ namespace Netch.Controllers
public static event EventHandler? NewVersionNotFound;
public static async Task Check(bool isPreRelease)
public static async Task CheckAsync(bool isPreRelease)
{
try
{
var updater = new GitHubRelease(Owner, Repo);
var url = updater.AllReleaseUrl;
var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url));
var (_, json) = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url));
var releases = JsonSerializer.Deserialize<List<Release>>(json)!;
LatestRelease = GetLatestRelease(releases, isPreRelease);
@@ -52,12 +52,12 @@ namespace Netch.Controllers
if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0)
{
Log.Information("发现新版本");
NewVersionFound?.Invoke(null, new EventArgs());
NewVersionFound?.Invoke(null, EventArgs.Empty);
}
else
{
Log.Information("目前是最新版本");
NewVersionNotFound?.Invoke(null, new EventArgs());
NewVersionNotFound?.Invoke(null, EventArgs.Empty);
}
}
catch (Exception e)
@@ -67,7 +67,7 @@ namespace Netch.Controllers
else
Log.Error(e, "获取新版本异常");
NewVersionFoundFailed?.Invoke(null, new EventArgs());
NewVersionFoundFailed?.Invoke(null, EventArgs.Empty);
}
}

View File

@@ -1,8 +1,9 @@
using System;
using System.ComponentModel;
using System.Windows.Forms;
using Vanara.PInvoke;
using static Vanara.PInvoke.User32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
using static Windows.Win32.PInvoke;
namespace Netch.Forms
{
@@ -34,21 +35,21 @@ namespace Netch.Forms
private void Parent_Activated(object? sender, EventArgs? e)
{
SetWindowPos(Handle,
HWND.HWND_TOPMOST,
SetWindowPos(new HWND(Handle),
new HWND(-1),
0,
0,
0,
0,
SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_SHOWWINDOW);
SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE | SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE | SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW);
SetWindowPos(Handle,
HWND.HWND_NOTOPMOST,
SetWindowPos(new HWND(Handle),
new HWND(-2),
0,
0,
0,
0,
SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_SHOWWINDOW);
SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE | SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE | SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW);
}
private void richTextBox1_TextChanged(object? sender, EventArgs? e)

View File

@@ -9,6 +9,10 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.WindowsAndMessaging;
using Microsoft.VisualStudio.Threading;
using Microsoft.Win32;
using Netch.Controllers;
using Netch.Enums;
@@ -19,7 +23,6 @@ using Netch.Properties;
using Netch.Services;
using Netch.Utils;
using Serilog;
using Vanara.PInvoke;
namespace Netch.Forms
{
@@ -50,7 +53,8 @@ namespace Netch.Forms
private void AddAddServerToolStripMenuItems()
{
foreach (var serversUtil in ServerHelper.ServerUtils.Where(i => !string.IsNullOrEmpty(i.FullName)))
foreach (var serversUtil in ServerHelper.ServerUtilDictionary.Values.OrderBy(i => i.Priority)
.Where(i => !string.IsNullOrEmpty(i.FullName)))
{
var fullName = serversUtil.FullName;
var control = new ToolStripMenuItem
@@ -90,23 +94,17 @@ namespace Netch.Forms
// 加载快速配置
LoadProfiles();
BeginInvoke(new Action(async () =>
{
// 检查更新
if (Global.Settings.CheckUpdateWhenOpened)
await CheckUpdate();
}));
// 检查更新
if (Global.Settings.CheckUpdateWhenOpened)
CheckUpdateAsync().Forget();
BeginInvoke(new Action(async () =>
{
// 检查订阅更新
if (Global.Settings.UpdateServersWhenOpened)
await UpdateServersFromSubscribe();
// 检查订阅更新
if (Global.Settings.UpdateServersWhenOpened)
UpdateServersFromSubscribeAsync().Forget();
// 打开软件时启动加速,产生开始按钮点击事件
if (Global.Settings.StartWhenOpened)
ControlButton_Click(null, null);
}));
// 打开软件时启动加速,产生开始按钮点击事件
if (Global.Settings.StartWhenOpened)
ControlButton.PerformClick();
Netch.SingleInstance.ListenForArgumentsFromSuccessiveInstances();
}
@@ -272,10 +270,10 @@ namespace Netch.Forms
private async void UpdateServersFromSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
{
await UpdateServersFromSubscribe();
await UpdateServersFromSubscribeAsync();
}
private async Task UpdateServersFromSubscribe()
private async Task UpdateServersFromSubscribeAsync()
{
void DisableItems(bool v)
{
@@ -330,7 +328,7 @@ namespace Netch.Forms
{
UpdateChecker.NewVersionNotFound += OnNewVersionNotFound;
UpdateChecker.NewVersionFoundFailed += OnNewVersionFoundFailed;
await CheckUpdate();
await CheckUpdateAsync();
}
finally
{
@@ -391,9 +389,9 @@ namespace Netch.Forms
private void ShowHideConsoleToolStripMenuItem_Click(object sender, EventArgs e)
{
var windowStyles = (User32.WindowStyles)User32.GetWindowLong(Netch.ConsoleHwnd, User32.WindowLongFlags.GWL_STYLE);
var visible = windowStyles.HasFlag(User32.WindowStyles.WS_VISIBLE);
User32.ShowWindow(Netch.ConsoleHwnd, visible ? ShowWindowCommand.SW_HIDE : ShowWindowCommand.SW_SHOWNOACTIVATE);
var windowStyles = (WINDOW_STYLE)PInvoke.GetWindowLong(new HWND(Netch.ConsoleHwnd), WINDOW_LONG_PTR_INDEX.GWL_STYLE);
var visible = windowStyles.HasFlag(WINDOW_STYLE.WS_VISIBLE);
PInvoke.ShowWindow(Netch.ConsoleHwnd, visible ? SHOW_WINDOW_CMD.SW_HIDE : SHOW_WINDOW_CMD.SW_SHOWNOACTIVATE);
}
#endregion
@@ -464,7 +462,7 @@ namespace Netch.Forms
}
ModeHelper.SuspendWatcher = true;
await Stop();
await StopAsync();
await Configuration.SaveAsync();
// Update
@@ -511,11 +509,11 @@ namespace Netch.Forms
{
if (!IsWaiting())
{
await StopCore();
await StopCoreAsync();
return;
}
await Configuration.SaveAsync();
Configuration.SaveAsync().Forget();
// 服务器、模式 需选择
if (ServerComboBox.SelectedItem is not Server server)
@@ -547,28 +545,31 @@ namespace Netch.Forms
State = State.Started;
Task.Run(Bandwidth.NetTraffic).Forget();
Task.Run(NatTest).Forget();
NatTestAsync().Forget();
if (Global.Settings.MinimizeWhenStarted)
Minimize();
// 自动检测延迟
Task.Run(() =>
async Task StartedPingAsync()
{
while (State == State.Started)
{
while (State == State.Started)
if (Global.Settings.StartedPingInterval >= 0)
{
server.Test();
ServerComboBox.Refresh();
if (Global.Settings.StartedPingInterval >= 0)
{
await server.PingAsync();
ServerComboBox.Refresh();
Thread.Sleep(Global.Settings.StartedPingInterval * 1000);
}
else
{
Thread.Sleep(5000);
}
})
.Forget();
await Task.Delay(Global.Settings.StartedPingInterval * 1000);
}
else
{
await Task.Delay(5000);
}
}
}
StartedPingAsync().Forget();
}
#endregion
@@ -636,9 +637,6 @@ namespace Netch.Forms
return;
}
if (!server.Valid())
return;
Hide();
ServerHelper.GetUtilByTypeName(server.Type).Edit(server);
LoadServers();
@@ -646,7 +644,7 @@ namespace Netch.Forms
Show();
}
private void SpeedPictureBox_Click(object sender, EventArgs e)
private async void SpeedPictureBox_Click(object sender, EventArgs e)
{
void Enable()
{
@@ -660,19 +658,13 @@ namespace Netch.Forms
if (!IsWaiting() || ModifierKeys == Keys.Control)
{
(ServerComboBox.SelectedItem as Server)?.Test();
(ServerComboBox.SelectedItem as Server)?.PingAsync();
Enable();
}
else
{
ServerHelper.DelayTestHelper.TestDelayFinished += OnTestDelayFinished;
Task.Run(ServerHelper.DelayTestHelper.TestAllDelay).Forget();
void OnTestDelayFinished(object? o1, EventArgs? e1)
{
ServerHelper.DelayTestHelper.TestDelayFinished -= OnTestDelayFinished;
Enable();
}
await ServerHelper.DelayTestHelper.TestAllDelayAsync();
Enable();
}
}
@@ -685,9 +677,6 @@ namespace Netch.Forms
return;
}
if (!server.Valid())
return;
try
{
//听说巨硬BUG经常会炸所以Catch一下 :D
@@ -1053,15 +1042,15 @@ namespace Netch.Forms
}
}
public async Task Stop()
public async Task StopAsync()
{
if (IsWaiting())
return;
await StopCore();
await StopCoreAsync();
}
private async Task StopCore()
private async Task StopCoreAsync()
{
State = State.Stopping;
await MainController.StopAsync();
@@ -1110,14 +1099,8 @@ namespace Netch.Forms
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = state;
}
public void NatTypeStatusText(string? text = null, string? country = null)
private void NatTypeStatusText(string? text = null, string? country = null)
{
if (InvokeRequired)
{
BeginInvoke(new Action<string, string>(NatTypeStatusText), text, country);
return;
}
if (State != State.Started)
{
NatTypeStatusLabel.Text = "";
@@ -1168,7 +1151,7 @@ namespace Netch.Forms
private async void NatTypeStatusLabel_Click(object sender, EventArgs e)
{
if (_state == State.Started && !Monitor.IsEntered(_natTestLock))
await NatTest();
await NatTestAsync();
}
private bool _natTestLock = true;
@@ -1176,7 +1159,7 @@ namespace Netch.Forms
/// <summary>
/// 测试 NAT
/// </summary>
private async Task NatTest()
private async Task NatTestAsync()
{
if (!MainController.Mode!.TestNatRequired())
return;
@@ -1190,11 +1173,11 @@ namespace Netch.Forms
{
NatTypeStatusText(i18N.Translate("Testing NAT"));
var (result, _, publicEnd) = await MainController.NTTController.Start();
var (result, _, publicEnd) = await MainController.NTTController.StartAsync();
if (!string.IsNullOrEmpty(publicEnd))
{
var country = Utils.Utils.GetCityCode(publicEnd!);
var country = await Utils.Utils.GetCityCodeAsync(publicEnd!);
NatTypeStatusText(result, country);
}
else
@@ -1281,7 +1264,7 @@ namespace Netch.Forms
if (File.Exists(file))
File.Delete(file);
await Stop();
await StopAsync();
Dispose();
Environment.Exit(Environment.ExitCode);
@@ -1311,12 +1294,12 @@ namespace Netch.Forms
#region Updater
private async Task CheckUpdate()
private async Task CheckUpdateAsync()
{
try
{
UpdateChecker.NewVersionFound += OnUpdateCheckerOnNewVersionFound;
await UpdateChecker.Check(Global.Settings.CheckBetaUpdate);
await UpdateChecker.CheckAsync(Global.Settings.CheckBetaUpdate);
if (Flags.AlwaysShowNewVersionFound)
OnUpdateCheckerOnNewVersionFound(null!, null!);
}

View File

@@ -74,7 +74,7 @@ namespace Netch.Forms
AddSaveButton();
i18N.TranslateForm(this);
ConfigurationGroupBox.Enabled = string.IsNullOrEmpty(Server.Remark);
ConfigurationGroupBox.Enabled = !Server.IsInGroup();
ConfigurationGroupBox.ResumeLayout(false);
ConfigurationGroupBox.PerformLayout();

View File

@@ -1,9 +1,11 @@
namespace Netch.Interfaces
using System.Threading.Tasks;
namespace Netch.Interfaces
{
public interface IController
{
public string Name { get; }
public void Stop();
public Task StopAsync();
}
}

View File

@@ -1,9 +1,10 @@
using System.Threading.Tasks;
using Netch.Models;
namespace Netch.Interfaces
{
public interface IModeController : IController
{
public void Start(Server server, Mode mode);
public Task StartAsync(Server server, Mode mode);
}
}

View File

@@ -1,4 +1,5 @@
using Netch.Models;
using System.Threading.Tasks;
using Netch.Models;
using Netch.Servers;
namespace Netch.Interfaces
@@ -9,7 +10,7 @@ namespace Netch.Interfaces
public string? LocalAddress { get; set; }
public Socks5 Start(in Server s);
public Task<Socks5> StartAsync(Server s);
}
public static class ServerControllerExtension

View File

@@ -1,5 +1,6 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Serilog;
namespace Netch.Interops
@@ -10,10 +11,20 @@ namespace Netch.Interops
public static bool Dial(NameList name, string value)
{
Log.Debug($"[aiodns] Dial {name}: {value}");
Log.Verbose($"[aiodns] Dial {name}: {value}");
return aiodns_dial(name, Encoding.UTF8.GetBytes(value));
}
public static async Task<bool> InitAsync()
{
return await Task.Run(Init).ConfigureAwait(false);
}
public static async Task FreeAsync()
{
await Task.Run(Free).ConfigureAwait(false);
}
[DllImport(aiodns_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern bool aiodns_dial(NameList name, byte[] value);

View File

@@ -1,4 +1,5 @@
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Serilog;
namespace Netch.Interops
@@ -43,18 +44,18 @@ namespace Netch.Interops
public static bool Dial(NameList name, string value)
{
Log.Debug($"[Redirector] Dial {name}: {value}");
Log.Verbose($"[Redirector] Dial {name}: {value}");
return aio_dial(name, value);
}
public static bool Init()
public static async Task<bool> InitAsync()
{
return aio_init();
return await Task.Run(aio_init).ConfigureAwait(false);
}
public static bool Free()
public static async Task<bool> FreeAsync()
{
return aio_free();
return await Task.Run(aio_free).ConfigureAwait(false);
}
public const int UdpNameListOffset = (int)NameList.TYPE_UDPLISN - (int)NameList.TYPE_TCPLISN;

View File

@@ -1,5 +1,6 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Serilog;
namespace Netch.Interops
@@ -36,19 +37,19 @@ namespace Netch.Interops
public static bool Dial(NameList name, string value)
{
Log.Debug( $"[tun2socks] Dial {name}: {value}");
Log.Verbose( $"[tun2socks] Dial {name}: {value}");
return tun_dial(name, Encoding.UTF8.GetBytes(value));
}
public static bool Init()
{
Log.Debug("[tun2socks] init");
Log.Verbose("[tun2socks] init");
return tun_init();
}
public static bool Free()
public static async Task<bool> FreeAsync()
{
return tun_free();
return await Task.Run(tun_free).ConfigureAwait(false);
}
private const string tun2socks_bin = "tun2socks.bin";

View File

@@ -1,6 +1,6 @@
using System;
using System.Net;
using Vanara.PInvoke;
using Windows.Win32;
namespace Netch.Models
{
@@ -18,10 +18,10 @@ namespace Netch.Models
public static NetRoute GetBestRouteTemplate()
{
if (IpHlpApi.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse("114.114.114.114").GetAddressBytes(), 0), 0, out var route) != 0)
if (PInvoke.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse("114.114.114.114").GetAddressBytes(), 0), 0, out var route) != 0)
throw new MessageException("GetBestRoute 搜索失败");
var gateway = new IPAddress(route.dwForwardNextHop.S_un_b);
var gateway = new IPAddress(route.dwForwardNextHop);
return TemplateBuilder(gateway.ToString(), (int)route.dwForwardIfIndex);
}

View File

@@ -17,7 +17,7 @@ namespace Netch.Models
/// <summary>
/// 组
/// </summary>
public string Group { get; set; } = "None";
public string Group { get; set; } = Constants.DefaultGroup;
/// <summary>
/// 地址
@@ -48,7 +48,6 @@ namespace Netch.Models
// ReSharper disable once CollectionNeverUpdated.Global
public Dictionary<string, object> ExtensionData { get; set; } = new();
public object Clone()
{
return MemberwiseClone();
@@ -62,56 +61,48 @@ namespace Netch.Models
{
var remark = string.IsNullOrWhiteSpace(Remark) ? $"{Hostname}:{Port}" : Remark;
if (Group.Equals("None") || Group.Equals(""))
Group = "NONE";
string shortName;
if (Type == string.Empty)
{
shortName = "WTF";
}
else
{
shortName = ServerHelper.GetUtilByTypeName(Type).ShortName;
}
var shortName = Type.IsNullOrEmpty() ? "WTF" : ServerHelper.GetUtilByTypeName(Type).ShortName;
return $"[{shortName}][{Group}] {remark}";
}
public abstract string MaskedData();
/// <summary>
/// 测试延迟
/// </summary>
/// <returns>延迟</returns>
public int Test()
public async Task<int> PingAsync()
{
try
{
var destination = DnsUtils.Lookup(Hostname);
var destination = await DnsUtils.LookupAsync(Hostname);
if (destination == null)
return Delay = -2;
var list = new Task<int>[3];
for (var i = 0; i < 3; i++)
list[i] = Task.Run(async () =>
{
async Task<int> PingCoreAsync()
{
try
{
return Global.Settings.ServerTCPing
? await Utils.Utils.TCPingAsync(destination, Port)
: Utils.Utils.ICMPing(destination, Port);
: await Utils.Utils.ICMPingAsync(destination);
}
catch (Exception)
{
return -4;
}
});
}
Task.WaitAll(list[0], list[1], list[2]);
list[i] = PingCoreAsync();
}
var min = Math.Min(list[0].Result, list[1].Result);
min = Math.Min(min, list[2].Result);
return Delay = min;
var resTask = await Task.WhenAny(list[0], list[1], list[2]);
return Delay = await resTask;
}
catch (Exception)
{
@@ -122,22 +113,14 @@ namespace Netch.Models
public static class ServerExtension
{
public static string AutoResolveHostname(this Server server)
public static async Task<string> AutoResolveHostnameAsync(this Server server)
{
return Global.Settings.ResolveServerHostname ? DnsUtils.Lookup(server.Hostname)!.ToString() : server.Hostname;
return Global.Settings.ResolveServerHostname ? (await DnsUtils.LookupAsync(server.Hostname))!.ToString() : server.Hostname;
}
public static bool Valid(this Server server)
public static bool IsInGroup(this Server server)
{
try
{
ServerHelper.GetTypeByTypeName(server.Type);
return true;
}
catch
{
return false;
}
return server.Group is not Constants.DefaultGroup;
}
}
}

21
Netch/NativeMethods.txt Normal file
View File

@@ -0,0 +1,21 @@
// IpHlpApi.dll
GetBestRoute
GetExtendedTcpTable
MIB_TCPTABLE_OWNER_PID
ADDRESS_FAMILY
// User32.dll
SetWindowPos
GetWindowLong
ShowWindow
WINDOW_STYLE
// Kernel32.dll
AllocConsole
GetConsoleWindow
// Ws2_32.dll
ntohs
// Windows.h
// WIN32_ERROR

View File

@@ -1,25 +1,32 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Windows.Win32;
using Windows.Win32.Foundation;
using Microsoft.VisualStudio.Threading;
using Netch.Controllers;
using Netch.Forms;
using Netch.Services;
using Netch.Utils;
using Serilog;
using Serilog.Events;
using Vanara.PInvoke;
using SingleInstance;
#if RELEASE
using Windows.Win32.UI.WindowsAndMessaging;
#endif
namespace Netch
{
public static class Netch
{
public static readonly SingleInstance.SingleInstanceService SingleInstance = new($"Global\\{nameof(Netch)}");
public static readonly SingleInstanceService SingleInstance = new($"Global\\{nameof(Netch)}");
public static HWND ConsoleHwnd { get; private set; }
internal static HWND ConsoleHwnd { get; private set; }
/// <summary>
/// 应用程序的主入口点
@@ -38,13 +45,15 @@ namespace Netch
Updater.CleanOld(Global.NetchDir);
// 预创建目录
var directories = new[] {"mode\\Custom", "data", "i18n", "logging"};
var directories = new[] { "mode\\Custom", "data", "i18n", "logging" };
foreach (var item in directories)
if (!Directory.Exists(item))
Directory.CreateDirectory(item);
// 加载配置
#pragma warning disable VSTHRD002
Configuration.LoadAsync().Wait();
#pragma warning restore VSTHRD002
if (!SingleInstance.IsFirstInstance)
{
@@ -80,7 +89,7 @@ namespace Netch
Environment.Exit(2);
}
Task.Run(LogEnvironment);
Task.Run(LogEnvironment).Forget();
// 绑定错误捕获
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
@@ -92,22 +101,24 @@ namespace Netch
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(Global.MainForm);
}
private static void LogEnvironment()
{
Log.Information("Netch Version: {Version}", $"{UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
Log.Information("Environment: {OSVersion}", Environment.OSVersion);
Log.Information("SHA256: {Hash}", $"{Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}");
Log.Information("System Language: {Language}", CultureInfo.CurrentCulture.Name);
if (Log.IsEnabled(LogEventLevel.Debug))
Log.Debug("Third-party Drivers:\n{Drivers}", string.Join("\n", SystemInfo.SystemDrivers(false)));
}
private static void InitConsole()
{
Kernel32.AllocConsole();
PInvoke.AllocConsole();
ConsoleHwnd = Kernel32.GetConsoleWindow();
ConsoleHwnd = PInvoke.GetConsoleWindow();
#if RELEASE
User32.ShowWindow(ConsoleHwnd, ShowWindowCommand.SW_HIDE);
PInvoke.ShowWindow(ConsoleHwnd, SHOW_WINDOW_CMD.SW_HIDE);
#endif
}

View File

@@ -10,6 +10,7 @@
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<NoWarn>VSTHRD100</NoWarn>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<AnalysisMode>Default</AnalysisMode>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
@@ -39,6 +40,11 @@
<PackageReference Include="HMBSbige.SingleInstance" Version="5.0.7" />
<PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.70" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="16.10.56" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.1.506-beta">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Nullable.Extended.Analyzer" Version="1.2.4089">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -52,9 +58,7 @@
<PackageReference Include="System.Management" Version="5.0.0" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
<PackageReference Include="TaskScheduler" Version="2.9.1" />
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.10" />
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.10" />
<PackageReference Include="WindowsFirewallHelper" Version="2.0.4.70-beta2" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="5.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
@@ -91,6 +95,10 @@
<Target Condition="'$(PublishSingleFile)' == 'true'" AfterTargets="_ComputeFilesToBundle" Name="RemoveDupeAssemblies">
<ItemGroup>
<_FilesToBundle Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\build\native\x86\**" />
<!-- .NET 6 SDK fixes MSB4018 "Multiple entries with the same BundleRelativePath",
if still retain the following properties,
you will encounter a "lib/netstandard2.0/Dial2Lib.dll not found" error
-->
<_FilesToBundle Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\lib\netstandard1.6\Dia2Lib.dll" />
<_FilesToBundle Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\lib\netstandard1.6\OSExtensions.dll" />
<_FilesToBundle Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\lib\netstandard1.6\TraceReloggerLib.dll" />

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models;
@@ -22,13 +23,13 @@ namespace Netch.Servers.Shadowsocks
public string? LocalAddress { get; set; }
public Socks5 Start(in Server s)
public async Task<Socks5> StartAsync(Server s)
{
var server = (Shadowsocks)s;
var command = new SSParameter
{
s = server.AutoResolveHostname(),
s = await server.AutoResolveHostnameAsync(),
p = server.Port,
b = this.LocalAddress(),
l = this.Socks5LocalPort(),
@@ -39,7 +40,7 @@ namespace Netch.Servers.Shadowsocks
plugin_opts = server.PluginOption
};
StartGuard(command.ToString());
await StartGuardAsync(command.ToString());
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname);
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models;
@@ -22,13 +23,13 @@ namespace Netch.Servers.ShadowsocksR
public string? LocalAddress { get; set; }
public Socks5 Start(in Server s)
public async Task<Socks5> StartAsync(Server s)
{
var server = (ShadowsocksR)s;
var command = new SSRParameter
{
s = server.AutoResolveHostname(),
s = await server.AutoResolveHostnameAsync(),
p = server.Port,
k = server.Password,
m = server.EncryptMethod,
@@ -42,7 +43,7 @@ namespace Netch.Servers.ShadowsocksR
u = true
};
StartGuard(command.ToString());
await StartGuardAsync(command.ToString());
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(),server.Hostname);
}

View File

@@ -1,5 +1,5 @@
using System.Threading.Tasks;
using Netch.Models;
using Netch.Servers;
namespace Netch.Servers
{
@@ -7,11 +7,11 @@ namespace Netch.Servers
{
public override string Name { get; } = "Socks5";
public override Socks5 Start(in Server s)
public override async Task<Socks5> StartAsync(Server s)
{
var server = (Socks5)s;
if (server.Auth())
base.Start(s);
await base.StartAsync(s);
return server;
}

View File

@@ -2,6 +2,7 @@
using System.IO;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models;
@@ -26,14 +27,14 @@ namespace Netch.Servers
public string? LocalAddress { get; set; }
public Socks5 Start(in Server s)
public async Task<Socks5> StartAsync(Server s)
{
var server = (Trojan)s;
var trojanConfig = new TrojanConfig
{
local_addr = this.LocalAddress(),
local_port = this.Socks5LocalPort(),
remote_addr = server.AutoResolveHostname(),
remote_addr = await server.AutoResolveHostnameAsync(),
remote_port = server.Port,
password = new List<string>
{
@@ -45,12 +46,12 @@ namespace Netch.Servers
}
};
using (var fileStream = new FileStream(Constants.TempConfig, FileMode.Create, FileAccess.Write))
await using (var fileStream = new FileStream(Constants.TempConfig, FileMode.Create, FileAccess.Write, FileShare.Read))
{
JsonSerializer.SerializeAsync(fileStream, trojanConfig, Global.NewCustomJsonSerializerOptions()).Wait();
await JsonSerializer.SerializeAsync(fileStream, trojanConfig, Global.NewCustomJsonSerializerOptions());
}
StartGuard("-c ..\\data\\last.json");
await StartGuardAsync("-c ..\\data\\last.json");
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname);
}
}

View File

@@ -1,13 +1,16 @@
using Netch.Models;
using System.Threading.Tasks;
using Netch.Models;
using Netch.Servers.V2ray.Models;
using Netch.Utils;
using V2rayConfig = Netch.Servers.V2ray.Models.V2rayConfig;
#pragma warning disable VSTHRD200
namespace Netch.Servers.Utils
{
public static class V2rayConfigUtils
{
public static V2rayConfig GenerateClientConfig(Server server)
public static async Task<V2rayConfig> GenerateClientConfigAsync(Server server)
{
var v2rayConfig = new V2rayConfig
{
@@ -26,12 +29,12 @@ namespace Netch.Servers.Utils
}
};
v2rayConfig.outbounds = new[] { outbound(server) };
v2rayConfig.outbounds = new[] { await outbound(server) };
return v2rayConfig;
}
private static Outbound outbound(Server server)
private static async Task<Outbound> outbound(Server server)
{
var outbound = new Outbound
{
@@ -48,7 +51,7 @@ namespace Netch.Servers.Utils
{
new
{
address = server.AutoResolveHostname(),
address = await server.AutoResolveHostnameAsync(),
port = server.Port,
users = socks5.Auth()
? new[]
@@ -75,7 +78,7 @@ namespace Netch.Servers.Utils
{
new VnextItem
{
address = server.AutoResolveHostname(),
address = await server.AutoResolveHostnameAsync(),
port = server.Port,
users = new[]
{
@@ -111,7 +114,7 @@ namespace Netch.Servers.Utils
{
new VnextItem
{
address = server.AutoResolveHostname(),
address = await server.AutoResolveHostnameAsync(),
port = server.Port,
users = new[]
{

View File

@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models;
@@ -27,14 +28,14 @@ namespace Netch.Servers
public string? LocalAddress { get; set; }
public virtual Socks5 Start(in Server s)
public virtual async Task<Socks5> StartAsync(Server s)
{
using (var fileStream = new FileStream(Constants.TempConfig, FileMode.Create, FileAccess.Write))
await using (var fileStream = new FileStream(Constants.TempConfig, FileMode.Create, FileAccess.Write, FileShare.Read))
{
JsonSerializer.SerializeAsync(fileStream, V2rayConfigUtils.GenerateClientConfig(s), Global.NewCustomJsonSerializerOptions()).Wait();
await JsonSerializer.SerializeAsync(fileStream, await V2rayConfigUtils.GenerateClientConfigAsync(s), Global.NewCustomJsonSerializerOptions());
}
StartGuard("-config ..\\data\\last.json");
await StartGuardAsync("-config ..\\data\\last.json");
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), s.Hostname);
}
}

View File

@@ -5,6 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Session;
using Microsoft.VisualStudio.Threading;
using Netch.Controllers;
using Netch.Models;
using Serilog;
@@ -52,69 +53,68 @@ namespace Netch.Utils
var counterLock = new object();
//int sent = 0;
//var processList = Process.GetProcessesByName(ProcessName).Select(p => p.Id).ToHashSet();
var instances = new List<Process>();
var processes = new List<Process>();
switch (MainController.ServerController)
{
case null:
break;
case Guard guard:
instances.Add(guard.Instance);
processes.Add(guard.Instance);
break;
}
if (!instances.Any())
if (!processes.Any())
switch (MainController.ModeController)
{
case null:
break;
case NFController:
instances.Add(Process.GetCurrentProcess());
case NFController or TUNController:
processes.Add(Process.GetCurrentProcess());
break;
case Guard guard:
instances.Add(guard.Instance);
processes.Add(guard.Instance);
break;
}
var processList = instances.Select(instance => instance.Id).ToHashSet();
var pidHastSet = processes.Select(instance => instance.Id).ToHashSet();
Log.Information("流量统计进程: {Processes}", string.Join(',', instances.Select(v => $"({v.Id}){v.ProcessName}")));
Log.Information("流量统计进程: {Processes}", string.Join(',', processes.Select(v => $"({v.Id}){v.ProcessName}")));
received = 0;
if (!instances.Any())
if (!processes.Any())
return;
Global.MainForm.BandwidthState(true);
Task.Run(() =>
{
tSession = new TraceEventSession("KernelAndClrEventsSession");
tSession.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP);
//这玩意儿上传和下载得到的data是一样的:)
//所以暂时没办法区分上传下载流量
tSession.Source.Kernel.TcpIpRecv += data =>
{
if (processList.Contains(data.ProcessID))
lock (counterLock)
received += (ulong)data.size;
tSession = new TraceEventSession("KernelAndClrEventsSession");
tSession.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP);
// Debug.WriteLine($"TcpIpRecv: {ToByteSize(data.size)}");
};
//这玩意儿上传和下载得到的data是一样的:)
//所以暂时没办法区分上传下载流量
tSession.Source.Kernel.TcpIpRecv += data =>
{
if (pidHastSet.Contains(data.ProcessID))
lock (counterLock)
received += (ulong)data.size;
tSession.Source.Kernel.UdpIpRecv += data =>
{
if (processList.Contains(data.ProcessID))
lock (counterLock)
received += (ulong)data.size;
// Debug.WriteLine($"TcpIpRecv: {ToByteSize(data.size)}");
};
// Debug.WriteLine($"UdpIpRecv: {ToByteSize(data.size)}");
};
tSession.Source.Kernel.UdpIpRecv += data =>
{
if (pidHastSet.Contains(data.ProcessID))
lock (counterLock)
received += (ulong)data.size;
tSession.Source.Process();
});
// Debug.WriteLine($"UdpIpRecv: {ToByteSize(data.size)}");
};
tSession.Source.Process();
})
.Forget();
while (Global.MainForm.State != State.Stopped)
{

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Threading;
using Netch.Models;
using Serilog;
@@ -24,6 +25,8 @@ namespace Netch.Utils
private const string BackupFileName = "settings.json.bak";
private static readonly AsyncReaderWriterLock _lock = new(null);
private static readonly JsonSerializerOptions JsonSerializerOptions = Global.NewCustomJsonSerializerOptions();
static Configuration()
@@ -42,11 +45,13 @@ namespace Netch.Utils
return;
}
if (await LoadAsyncCore(FileFullName))
await using var _ = await _lock.ReadLockAsync();
if (await LoadCoreAsync(FileFullName))
return;
Log.Information("尝试加载备份配置文件 {FileName}", BackupFileFullName);
await LoadAsyncCore(BackupFileFullName);
await LoadCoreAsync(BackupFileFullName);
}
catch (Exception e)
{
@@ -55,19 +60,20 @@ namespace Netch.Utils
}
}
private static async ValueTask<bool> LoadAsyncCore(string filename)
private static async ValueTask<bool> LoadCoreAsync(string filename)
{
try
{
var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
await using (fs.ConfigureAwait(false))
{
var settings = (await JsonSerializer.DeserializeAsync<Setting>(fs, JsonSerializerOptions).ConfigureAwait(false))!;
Setting settings;
CheckSetting(settings);
Global.Settings = settings;
return true;
await using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
{
settings = (await JsonSerializer.DeserializeAsync<Setting>(fs, JsonSerializerOptions).ConfigureAwait(false))!;
}
CheckSetting(settings);
Global.Settings = settings;
return true;
}
catch (Exception e)
{
@@ -93,18 +99,27 @@ namespace Netch.Utils
/// </summary>
public static async Task SaveAsync()
{
if (_lock.IsWriteLockHeld)
return;
try
{
await using var _ = await _lock.WriteLockAsync();
Log.Verbose("Save Configuration");
if (!Directory.Exists(DataDirectoryFullName))
Directory.CreateDirectory(DataDirectoryFullName);
var tempFile = Path.Combine(DataDirectoryFullName, FileFullName + ".tmp");
var fileStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true);
await using (var fileStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true))
await using (fileStream.ConfigureAwait(false))
{
await JsonSerializer.SerializeAsync(fileStream, Global.Settings, JsonSerializerOptions).ConfigureAwait(false);
}
if (!File.Exists(FileFullName))
File.Create(FileFullName);
File.Replace(tempFile, FileFullName, BackupFileFullName);
}
catch (Exception e)

View File

@@ -1,8 +1,12 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Serilog;
namespace Netch.Utils
{
@@ -12,27 +16,49 @@ namespace Netch.Utils
/// 缓存
/// </summary>
private static readonly Hashtable Cache = new();
private static readonly Hashtable Cache6 = new();
public static IPAddress? Lookup(string hostname, int timeout = 3000)
public static async Task<IPAddress?> LookupAsync(string hostname, AddressFamily inet = AddressFamily.InterNetwork, int timeout = 3000)
{
try
{
if (Cache.Contains(hostname))
return Cache[hostname] as IPAddress;
if (inet == AddressFamily.InterNetwork)
{
if (Cache.Contains(hostname))
return Cache[hostname] as IPAddress;
}
else
{
Trace.Assert(inet == AddressFamily.InterNetworkV6);
if (Cache6.Contains(hostname))
return Cache6[hostname] as IPAddress;
}
var task = Dns.GetHostAddressesAsync(hostname);
if (!task.Wait(timeout))
return null;
if (task.Result.Length == 0)
return null;
var resTask = await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false);
Cache.Add(hostname, task.Result[0]);
if (resTask == task)
{
var addresses = await task;
return task.Result[0];
var result = addresses.FirstOrDefault(i => i.AddressFamily == inet);
if (result == null)
return null;
if (inet == AddressFamily.InterNetwork)
Cache.Add(hostname, result);
else
Cache6.Add(hostname, result);
return result;
}
return null;
}
catch (Exception)
catch (Exception e)
{
Log.Verbose(e, "Lookup hostname {Hostname} failed", hostname);
return null;
}
}
@@ -45,6 +71,7 @@ namespace Netch.Utils
public static void ClearCache()
{
Cache.Clear();
Cache6.Clear();
}
public static IEnumerable<string> Split(string dns)

View File

@@ -5,9 +5,8 @@ using System.Management;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
using Netch.Models;
using Vanara.PInvoke;
using Windows.Win32;
namespace Netch.Utils
{
@@ -15,14 +14,18 @@ namespace Netch.Utils
{
public static NetworkInterface GetBest(AddressFamily addressFamily = AddressFamily.InterNetwork)
{
var ipAddress = addressFamily switch
string ipAddress;
if (addressFamily == AddressFamily.InterNetwork)
{
AddressFamily.InterNetwork => "114.114.114.114",
AddressFamily.InterNetworkV6 => throw new NotImplementedException(),
_ => throw new ArgumentOutOfRangeException(nameof(addressFamily), addressFamily, null)
};
ipAddress = "114.114.114.114";
}
else
{
Trace.Assert(addressFamily == AddressFamily.InterNetworkV6);
throw new NotImplementedException();
}
if (IpHlpApi.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0), 0, out var route) != 0)
if (PInvoke.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse(ipAddress).GetAddressBytes(), 0), 0, out var route) != 0)
throw new MessageException("GetBestRoute 搜索失败");
return Get((int)route.dwForwardIfIndex);

View File

@@ -1,12 +1,15 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.NetworkManagement.IpHelper;
using Netch.Models;
using Serilog;
using static Vanara.PInvoke.IpHlpApi;
using static Vanara.PInvoke.Ws2_32;
namespace Netch.Utils
{
@@ -29,14 +32,38 @@ namespace Netch.Utils
}
}
public static IEnumerable<Process> GetProcessByUsedTcpPort(ushort port)
internal static IEnumerable<Process> GetProcessByUsedTcpPort(ushort port, AddressFamily inet = AddressFamily.InterNetwork)
{
if (port == 0)
throw new ArgumentOutOfRangeException();
var row = GetTcpTable2().Where(r => ntohs((ushort)r.dwLocalPort) == port).Where(r => r.dwOwningPid is not (0 or 4));
if (inet != AddressFamily.InterNetwork)
Trace.Assert(inet == AddressFamily.InterNetworkV6);
return row.Select(r => Process.GetProcessById((int)r.dwOwningPid));
var process = new List<Process>();
unsafe
{
uint err;
uint size = 0;
PInvoke.GetExtendedTcpTable(default, ref size, false, (uint)inet, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_LISTENER, 0); // get size
var tcpTable = (MIB_TCPTABLE_OWNER_PID*)Marshal.AllocHGlobal((int)size);
if ((err = PInvoke.GetExtendedTcpTable(tcpTable, ref size, false, (uint)inet, TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_LISTENER, 0)) != 0)
throw new Win32Exception((int)err);
for (var i = 0; i < tcpTable -> dwNumEntries; i++)
{
var row = tcpTable -> table.ReadOnlyItemRef(i);
if (row.dwOwningPid is 0 or 4)
continue;
if (PInvoke.ntohs((ushort)row.dwLocalPort) == port)
process.Add(Process.GetProcessById((int)row.dwOwningPid));
}
}
return process;
}
private static void GetReservedPortRange(PortType portType, ref List<NumberRange> targetList)
@@ -109,7 +136,8 @@ namespace Netch.Utils
break;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
Trace.Assert(false);
return;
}
}
@@ -135,7 +163,8 @@ namespace Netch.Utils
break;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
Trace.Assert(false);
return;
}
}

View File

@@ -28,6 +28,14 @@ namespace Netch.Utils
public static bool CreateRoute(NetRoute o)
{
Log.Verbose("CreateRoute {InterNetwork} {Address} {Cidr} {Gateway} {Interface} {Metric}",
AddressFamily.InterNetwork,
o.Network,
o.Cidr,
o.Gateway,
(ulong)o.InterfaceIndex,
o.Metric);
return RouteHelper.CreateRoute(AddressFamily.InterNetwork, o.Network, o.Cidr, o.Gateway, (ulong)o.InterfaceIndex, o.Metric);
}
@@ -41,7 +49,7 @@ namespace Netch.Utils
{
if (!TryParseIPNetwork(rule, out var network, out var cidr))
{
Log.Warning("invalid rule {Rule}",rule);
Log.Warning("invalid rule {Rule}", rule);
return false;
}
@@ -50,6 +58,14 @@ namespace Netch.Utils
public static bool DeleteRoute(NetRoute o)
{
Log.Verbose("DeleteRoute {InterNetwork} {Address} {Cidr} {Gateway} {Interface} {Metric}",
AddressFamily.InterNetwork,
o.Network,
o.Cidr,
o.Gateway,
(ulong)o.InterfaceIndex,
o.Metric);
return RouteHelper.DeleteRoute(AddressFamily.InterNetwork, o.Network, o.Cidr, o.Gateway, (ulong)o.InterfaceIndex, o.Metric);
}

View File

@@ -16,6 +16,7 @@ namespace Netch.Utils
try
{
var type = ServerHelper.GetTypeByTypeName(jsonElement.GetProperty("Type").GetString()!);
// TODO replace with .NET 6 Deserialize from DOM
return (Server)JsonSerializer.Deserialize(jsonElement.GetRawText(), type)!;
}
catch

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Threading;
using Netch.Interfaces;
using Netch.Models;
using Timer = System.Timers.Timer;
@@ -18,12 +20,7 @@ namespace Netch.Utils
.GetExportedTypes()
.Where(type => type.GetInterfaces().Contains(typeof(IServerUtil)));
ServerUtils = serversUtilsTypes.Select(t => (IServerUtil)Activator.CreateInstance(t)!).OrderBy(util => util.Priority);
}
public static Type GetTypeByTypeName(string typeName)
{
return ServerUtils.Single(i => i.TypeName.Equals(typeName)).ServerType;
ServerUtilDictionary = serversUtilsTypes.Select(t => (IServerUtil)Activator.CreateInstance(t)!).ToDictionary(util => util.TypeName);
}
#region Delay
@@ -33,6 +30,8 @@ namespace Netch.Utils
private static readonly Timer Timer;
private static readonly object TestAllLock = new();
private static readonly SemaphoreSlim SemaphoreSlim = new(1, 16);
public static readonly NumberRange Range = new(0, int.MaxValue / 1000);
static DelayTestHelper()
@@ -43,7 +42,7 @@ namespace Netch.Utils
AutoReset = true
};
Timer.Elapsed += (_, _) => TestAllDelay();
Timer.Elapsed += (_, _) => TestAllDelayAsync().Forget();
}
public static bool Enabled
@@ -65,17 +64,27 @@ namespace Netch.Utils
return value != 0 && Range.InRange(value);
}
public static event EventHandler? TestDelayFinished;
public static void TestAllDelay()
public static async Task TestAllDelayAsync()
{
if (!Monitor.TryEnter(TestAllLock))
return;
try
{
Parallel.ForEach(Global.Settings.Server, new ParallelOptions { MaxDegreeOfParallelism = 16 }, server => { server.Test(); });
TestDelayFinished?.Invoke(null, new EventArgs());
var tasks = Global.Settings.Server.Select(async s =>
{
await SemaphoreSlim.WaitAsync();
try
{
await s.PingAsync();
}
finally
{
SemaphoreSlim.Release();
}
});
await Task.WhenAll(tasks);
}
catch (Exception)
{
@@ -95,8 +104,9 @@ namespace Netch.Utils
return;
Timer.Interval = Global.Settings.DetectionTick * 1000;
Task.Run(TestAllDelay);
Timer.Start();
TestAllDelayAsync().Forget();
}
}
@@ -104,27 +114,21 @@ namespace Netch.Utils
#region Handler
public static readonly IEnumerable<IServerUtil> ServerUtils;
public static Dictionary<string, IServerUtil> ServerUtilDictionary { get; set; }
public static IServerUtil GetUtilByTypeName(string typeName)
{
if (string.IsNullOrEmpty(typeName))
throw new ArgumentNullException();
return ServerUtils.Single(i => i.TypeName.Equals(typeName));
return ServerUtilDictionary[typeName];
}
public static IServerUtil GetUtilByFullName(string fullName)
public static IServerUtil? GetUtilByUriScheme(string scheme)
{
if (string.IsNullOrEmpty(fullName))
throw new ArgumentNullException();
return ServerUtils.Single(i => i.FullName.Equals(fullName));
return ServerUtilDictionary.Values.SingleOrDefault(i => i.UriScheme.Any(s => s.Equals(scheme)));
}
public static IServerUtil? GetUtilByUriScheme(string typeName)
public static Type GetTypeByTypeName(string typeName)
{
return ServerUtils.SingleOrDefault(i => i.UriScheme.Any(s => s.Equals(typeName)));
return GetUtilByTypeName(typeName).ServerType;
}
#endregion

View File

@@ -73,9 +73,9 @@ namespace Netch.Utils
return value.Split(separator, StringSplitOptions.RemoveEmptyEntries);
}
public static string? ValueOrDefault(this string? value)
public static string? ValueOrDefault(this string? value, string? defaultValue = default)
{
return string.IsNullOrWhiteSpace(value) ? null : value;
return string.IsNullOrWhiteSpace(value) ? defaultValue : value;
}
public static string[]? SplitOrDefault(this string? value)

View File

@@ -1,9 +1,9 @@
using Netch.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Netch.Models;
using Serilog;
namespace Netch.Utils
@@ -14,10 +14,10 @@ namespace Netch.Utils
public static async Task UpdateServersAsync(string? proxyServer = default)
{
await Task.WhenAll(Global.Settings.SubscribeLink.Select(item => Task.Run(() => UpdateServer(item, proxyServer))).ToArray());
await Task.WhenAll(Global.Settings.SubscribeLink.Select(item => UpdateServerCoreAsync(item, proxyServer)));
}
public static void UpdateServer(SubscribeLink item, string? proxyServer)
private static async Task UpdateServerCoreAsync(SubscribeLink item, string? proxyServer)
{
try
{
@@ -34,11 +34,11 @@ namespace Netch.Utils
List<Server> servers;
var result = WebUtil.DownloadString(request, out var rep);
if (rep.StatusCode == HttpStatusCode.OK)
var (code, result) = await WebUtil.DownloadStringAsync(request);
if (code == HttpStatusCode.OK)
servers = ShareLink.ParseText(result);
else
throw new Exception($"{item.Remark} Response Status Code: {rep.StatusCode}");
throw new Exception($"{item.Remark} Response Status Code: {code}");
foreach (var server in servers)
server.Group = item.Remark;

View File

@@ -1,11 +0,0 @@
using System.Threading.Tasks;
namespace Netch.Utils
{
public static class TplExtensions
{
public static void Forget(this Task? task)
{
}
}
}

View File

@@ -57,33 +57,34 @@ namespace Netch.Utils
return timeout;
}
public static int ICMPing(IPAddress ip, int timeout = 1000)
public static async Task<int> ICMPingAsync(IPAddress ip, int timeout = 1000)
{
var reply = new Ping().Send(ip, timeout);
var reply = await new Ping().SendPingAsync(ip, timeout);
if (reply?.Status == IPStatus.Success)
if (reply.Status == IPStatus.Success)
return Convert.ToInt32(reply.RoundtripTime);
return timeout;
}
public static string GetCityCode(string Hostname)
public static async Task<string> GetCityCodeAsync(string address)
{
if (Hostname.Contains(":"))
Hostname = Hostname.Split(':')[0];
var i = address.IndexOf(':');
if (i != -1)
address = address[..i];
string? country = null;
try
{
var databaseReader = new DatabaseReader("bin\\GeoLite2-Country.mmdb");
if (IPAddress.TryParse(Hostname, out _))
if (IPAddress.TryParse(address, out _))
{
country = databaseReader.Country(Hostname).Country.IsoCode;
country = databaseReader.Country(address).Country.IsoCode;
}
else
{
var dnsResult = DnsUtils.Lookup(Hostname);
var dnsResult = await DnsUtils.LookupAsync(address);
if (dnsResult != null)
country = databaseReader.Country(dnsResult).Country.IsoCode;

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Threading;
namespace Netch.Utils
{
@@ -37,9 +38,9 @@ namespace Netch.Utils
/// <returns></returns>
public static async Task<byte[]> DownloadBytesAsync(HttpWebRequest req)
{
using var webResponse = req.GetResponseAsync();
using var webResponse = await req.GetResponseAsync();
await using var memoryStream = new MemoryStream();
await using var input = webResponse.Result.GetResponseStream();
await using var input = webResponse.GetResponseStream();
await input.CopyToAsync(memoryStream);
return memoryStream.ToArray();
@@ -52,14 +53,14 @@ namespace Netch.Utils
/// <param name="rep"></param>
/// <param name="encoding">编码默认UTF-8</param>
/// <returns></returns>
public static string DownloadString(HttpWebRequest req, out HttpWebResponse rep, Encoding? encoding = null)
public static (HttpStatusCode, string) DownloadString(HttpWebRequest req, Encoding? encoding = null)
{
encoding ??= Encoding.UTF8;
rep = (HttpWebResponse)req.GetResponse();
using var rep = (HttpWebResponse)req.GetResponse();
using var responseStream = rep.GetResponseStream();
using var streamReader = new StreamReader(responseStream, encoding);
return streamReader.ReadToEnd();
return (rep.StatusCode, streamReader.ReadToEnd());
}
/// <summary>
@@ -68,14 +69,14 @@ namespace Netch.Utils
/// <param name="req"></param>
/// <param name="encoding">编码默认UTF-8</param>
/// <returns></returns>
public static async Task<string> DownloadStringAsync(HttpWebRequest req, Encoding? encoding = null)
public static async Task<(HttpStatusCode, string)> DownloadStringAsync(HttpWebRequest req, Encoding? encoding = null)
{
encoding ??= Encoding.UTF8;
using var webResponse = await req.GetResponseAsync();
using var webResponse = (HttpWebResponse)await req.GetResponseAsync();
await using var responseStream = webResponse.GetResponseStream();
using var streamReader = new StreamReader(responseStream, encoding);
return await streamReader.ReadToEndAsync();
return (webResponse.StatusCode, await streamReader.ReadToEndAsync());
}
public static async Task DownloadFileAsync(string address, string fileFullPath, IProgress<int>? progress = null)
@@ -91,7 +92,7 @@ namespace Netch.Utils
using (var downloadTask = input.CopyToAsync(fileStream))
{
if (progress != null)
ReportProgress(webResponse.ContentLength, downloadTask, fileStream, progress, 200).Forget();
ReportProgressAsync(webResponse.ContentLength, downloadTask, fileStream, progress, 200).Forget();
await downloadTask;
}
@@ -99,7 +100,7 @@ namespace Netch.Utils
progress?.Report(100);
}
private static async Task ReportProgress(long total, IAsyncResult downloadTask, Stream stream, IProgress<int> progress, int interval)
private static async Task ReportProgressAsync(long total, IAsyncResult downloadTask, Stream stream, IProgress<int> progress, int interval)
{
var n = 0;
while (!downloadTask.IsCompleted)

3
UnitTest/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/bin
/obj
/*.csproj.user

23
UnitTest/UnitTest.csproj Normal file
View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0-windows</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.5" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.5" />
<PackageReference Include="coverlet.collector" Version="3.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Netch\Netch.csproj" />
</ItemGroup>
</Project>

13
UnitTest/UnitTest1.cs Normal file
View File

@@ -0,0 +1,13 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
}
}
}

View File

@@ -22,11 +22,11 @@ param (
$PublishSingleFile = $True
)
.\scripts\download.ps1 $OutputPath
# .\scripts\download.ps1 $OutputPath
if ( -Not $? ) {
Exit 1
}
# if ( -Not $? ) {
# Exit 1
# }
Write-Host "Building $Configuration to $OutputPath"
dotnet publish `

5
global.json Normal file
View File

@@ -0,0 +1,5 @@
{
"sdk": {
"allowPrerelease": false
}
}