Compare commits

..

68 Commits
1.8.6 ... 1.9.2

Author SHA1 Message Date
ChsBuffer
0d535aa27e Bump version to 1.9.2 2021-10-15 20:44:50 +08:00
ChsBuffer
03c3c151bf Update CI 2021-10-15 20:44:07 +08:00
ChsBuffer
7a3b5ff487 Update Redirector settings
Update Redirector interop
Update NFController
Create Netch.JsonConverter namespace
2021-10-15 20:23:10 +08:00
ChsBuffer
598ff19535 Nice properties reuse, v2rayN. 2021-10-14 20:26:11 +08:00
ChsBuffer
d02135f31b Bump version to 1.9.1 2021-10-01 12:39:57 +08:00
ChsBuffer
a7756dabc1 Fix GetReservedPortRange 2021-10-01 12:39:46 +08:00
ChsBuffer
7ce1127006 Move bin folder check before create directories exist.
cleanup Core.bin strings
2021-10-01 12:30:57 +08:00
ChsBuffer
fec84a4952 Update NFController
Update ICMPDelay setting
Update WebUtil Default UserAgent
Hide unimplemented features' settings
Remove unused HTTP Port setting
2021-10-01 12:11:45 +08:00
ChsBuffer
d335c69fab Fix Import Server from Clipboard not set default group 2021-09-28 18:11:52 +08:00
ChsBuffer
1228a565c1 Update texts
rename Subscribe Link (WTF??) to Subscription
Full English log
Remove Debug Logging Sink
Add Console Logging Sink
2021-09-23 20:17:45 +08:00
ChsBuffer
dc904c9c0b Fix start failed deadlock 2021-09-21 17:16:40 +08:00
ChsBuffer
d829e347d3 Bump version to 1.9.0 2021-09-21 10:30:04 +08:00
ChsBuffer
a01761d2e2 Bump version to 1.8.9 2021-09-20 20:35:28 +08:00
ChsBuffer
68d87e2ff2 Remove Resolve Server Hostname setting 2021-09-20 20:34:23 +08:00
ChsBuffer
04d6933319 Rename TcpStatusLabel to HTTPStatusLabel 2021-09-20 19:08:01 +08:00
ChsBuffer
e46eef17d0 Create scripts\download\pcap2socks.ps1 2021-09-20 18:54:49 +08:00
ChsBuffer
d3c3958dab fix a typo 2021-09-20 18:20:59 +08:00
ChsBuffer
5ec8d38fd1 Fix build 2021-09-14 09:47:43 +08:00
ChsBuffer
2a8754ecfb Update build 2021-09-14 09:42:00 +08:00
ChsBuffer
cbc6822bff Refactor Cancel Connectivity Test 2021-09-14 08:49:37 +08:00
ChsBuffer
96bd7473ca Refactor AsyncLock 2021-09-14 08:41:30 +08:00
ChsBuffer
54b2b87dec Update Socks5ServerTestUtils.GetSimpleResult() 2021-09-12 03:18:35 +08:00
ChsBuffer
42baed8b8f Catch HttpConnectAsync's exception 2021-09-12 01:34:12 +08:00
ChsBuffer
f68aae6795 Update Setting.cs
- TUNTAP.UseCustomDNS true->false
- V2rayConfig.XrayCone false->true
- JsonIgnore Redirector.ChildProcessHandle
2021-09-12 01:32:48 +08:00
ChsBuffer
8e2008077d Bump version to 1.8.8 2021-09-11 01:32:41 +08:00
ChsBuffer
5b4f0026ff Feature: framework rollForward Major
this allows you to run .NET application targeting 5.x.x running on .NET 6.x.x runtime, but it's unsupported, May behave unexpectedly.
2021-09-11 01:28:27 +08:00
ChsBuffer
89f9dccb87 Fix virtual adapter mode NAT type test result "Wrong STUN server" 2021-09-11 01:19:01 +08:00
ChsBuffer
3e377f2e9d Feature: Server http connect time Test 2021-09-11 01:12:28 +08:00
ChsBuffer
635212f24d Replace NTTController with NatTypetester(Stun.Net)
Enable NAT Type Test for all mode types
Remove Shadowsocks SS
2021-09-10 23:56:25 +08:00
ChsBuffer
46d60babbc Refactor: Update Netch.Servers naming 2021-08-31 11:48:49 +08:00
ChsBuffer
8f80f9abef Update Nuget Packages 2021-08-29 03:54:19 +08:00
Netch
e268f1838f Update MainForm.cs 2021-08-14 09:18:27 +08:00
ChsBuffer
d99229ad50 Fix Netch.csproj loses ApplicationManifest property 2021-08-06 14:01:50 +08:00
ChsBuffer
df85d5797d Feature: remind will get no support from developers if OS is Windows 10 1809 below or CLR version is different from target framework 2021-08-06 06:59:19 +08:00
ChsBuffer
74856ccd61 Update UnitTest.csproj import common.props 2021-08-06 06:59:19 +08:00
ChsBuffer
0165d080c6 Feature: NFController.CheckCore check Core.bin file 2021-08-06 05:05:18 +08:00
ChsBuffer
97fb20e326 Update MainForm.State.StartDisableItems() 2021-08-06 04:55:16 +08:00
ChsBuffer
4d71e2d12f Bump Microsoft.Diagnostics.Tracing.TraceEvent from 2.0.70 to 2.0.71
Remove System.Drawing.Common nuget package
2021-08-06 04:45:59 +08:00
ChsBuffer
0fa83eac3c fix a typo 2021-08-06 02:13:32 +08:00
ChsBuffer
aa6623b063 Create common.props 2021-08-01 02:51:23 +08:00
ChsBuffer
94335ad900 Remove Properties\Settings.settings 2021-08-01 02:50:19 +08:00
ChsBuffer
baf3b39dd3 Fix Socks5 username and password not being saved to the configuration file 2021-08-01 02:49:00 +08:00
ChsBuffer
c12122f7d0 Refactor: Create ModeFeature Enum 2021-07-23 00:44:16 +08:00
ChsBuffer
3e5a4fc102 Update NatTestLock 2021-07-23 00:44:16 +08:00
ChsBuffer
57dbd0193a Move Netch.Models.State Netch.Models.LogLevel to Netch.Enums Namespace 2021-07-23 00:44:16 +08:00
ChsBuffer
5647a6c7ea Refactor SS,SSR Controller Generate Argument 2021-07-23 00:44:16 +08:00
ChsBuffer
773bad4845 Update ServerHelper.cs
Extract and Refactor DelayTestHelper
2021-07-23 00:44:15 +08:00
ChsBuffer
3e943ec6b8 Fix PcapController assert socks5 server false 2021-07-20 08:13:27 +08:00
ChsBuffer
920b068a1e Update DnsUtils 2021-07-19 08:09:57 +08:00
ChsBuffer
7eac7b0837 Fixup Touch Configuration File 2021-07-16 05:40:21 +08:00
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
115 changed files with 1896 additions and 1706 deletions

View File

@@ -1,5 +1,8 @@
name: Netch Build CI
on: [push, pull_request]
on:
push:
branches: [ master ]
pull_request:
jobs:
build:

View File

@@ -41,6 +41,9 @@ jobs:
Netch.7z
body: |
[![](https://img.shields.io/badge/Telegram-Channel-blue)](https://t.me/netch_channel) [![](https://img.shields.io/badge/Telegram-Group-green)](https://t.me/netch_group)
[**第一次使用请务必先安装 .NET 5.0 运行库**](https://aka.ms/dotnet/5.0/windowsdesktop-runtime-win-x64.exe)
## Changelogs
* This is an automated deployment of GitHub Actions, the change log should be updated manually soon

View File

@@ -1,10 +1,22 @@
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
common.props = common.props
global.json = global.json
LICENSE = LICENSE
README.md = README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -15,6 +27,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|x64
{38240783-9AD2-4A01-84C1-1A3E5F05720F}.Debug|x64.Build.0 = Debug|x64
{38240783-9AD2-4A01-84C1-1A3E5F05720F}.Release|x64.ActiveCfg = Release|x64
{38240783-9AD2-4A01-84C1-1A3E5F05720F}.Release|x64.Build.0 = Release|x64
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,9 @@ 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.Enums;
using Netch.Models;
using Netch.Utils;
using Serilog;
@@ -67,11 +68,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 +83,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 +96,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 +137,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,12 +152,12 @@ namespace Netch.Controllers
if (Instance is { HasExited: false })
{
Instance.Kill();
Instance.WaitForExit();
await Instance.WaitForExitAsync();
}
}
catch (Win32Exception e)
{
Log.Error(e, "停止 {Name} 异常", Instance.ProcessName);
Log.Error(e, "Stop {Name} failed", Instance.ProcessName);
}
catch
{

View File

@@ -1,54 +1,85 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Threading;
using Netch.Enums;
using Netch.Interfaces;
using Netch.Models;
using Netch.Servers;
using Netch.Utils;
using Serilog;
using Serilog.Events;
namespace Netch.Controllers
{
public static class MainController
{
public static Mode? Mode { get; private set; }
public static Socks5Server? Socks5Server { get; private set; }
public static readonly NTTController NTTController = new();
public static Server? Server { get; private set; }
public static Mode? Mode { get; private set; }
public static IServerController? ServerController { get; private set; }
public static IModeController? ModeController { get; private set; }
public static ModeFeature ModeFeatures { get; private set; }
private static readonly AsyncSemaphore Lock = new(1);
public static async Task StartAsync(Server server, Mode mode)
{
Log.Information("启动主控制器: {Server} {Mode}", $"{server.Type}", $"[{(int) mode.Type}]{mode.Remark}");
using var releaser = await Lock.EnterAsync();
if (DnsUtils.Lookup(server.Hostname) == null)
Log.Information("Start MainController: {Server} {Mode}", $"{server.Type}", $"[{(int)mode.Type}]{mode.Remark}");
if (await DnsUtils.LookupAsync(server.Hostname) == null)
throw new MessageException(i18N.Translate("Lookup Server hostname failed"));
// TODO Disable NAT Type Test setting
// cache STUN Server ip to prevent "Wrong STUN Server"
DnsUtils.LookupAsync(Global.Settings.STUN_Server).Forget();
Server = server;
Mode = mode;
await Task.WhenAll(
Task.Run(NativeMethods.RefreshDNSCache),
Task.Run(Firewall.AddNetchFwRules)
);
if (Log.IsEnabled(LogEventLevel.Debug))
Task.Run(() =>
{
// TODO log level setting
Log.Debug("Running Processes: \n{Processes}", string.Join("\n", SystemInfo.Processes(false)));
}).Forget();
await Task.WhenAll(Task.Run(NativeMethods.RefreshDNSCache), Task.Run(Firewall.AddNetchFwRules));
try
{
if (!ModeHelper.SkipServerController(server, mode))
server = await Task.Run(() => StartServer(server));
(ModeController, ModeFeatures) = ModeHelper.GetModeControllerByType(mode.Type, out var modePort, out var portName);
await Task.Run(() => StartMode(server, mode));
if (modePort != null)
TryReleaseTcpPort((ushort)modePort, portName);
if (Server is Socks5Server socks5 && (!socks5.Auth() || ModeFeatures.HasFlag(ModeFeature.SupportSocks5Auth)))
{
Socks5Server = socks5;
}
else
{
// Start Server Controller to get a local socks5 server
Log.Debug("Server Information: {Data}", $"{server.Type} {server.MaskedData()}");
ServerController = ServerHelper.GetUtilByTypeName(server.Type).GetController();
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ServerController.Name));
TryReleaseTcpPort(ServerController.Socks5LocalPort(), "Socks5");
Socks5Server = await ServerController.StartAsync(server);
StatusPortInfoText.Socks5Port = Socks5Server.Port;
StatusPortInfoText.UpdateShareLan();
}
// Start Mode Controller
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
await ModeController.StartAsync(Socks5Server, mode);
}
catch (Exception e)
{
releaser.Dispose();
await StopAsync();
switch (e)
@@ -59,54 +90,33 @@ 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)
{
ServerController = ServerHelper.GetUtilByTypeName(server.Type).GetController();
TryReleaseTcpPort(ServerController.Socks5LocalPort(), "Socks5");
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ServerController.Name));
Log.Debug($"{server.Type} {server.MaskedData()}");
var socks5 = ServerController.Start(server);
StatusPortInfoText.Socks5Port = socks5.Port;
StatusPortInfoText.UpdateShareLan();
return socks5;
}
private static void StartMode(Server server, Mode mode)
{
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName);
if (port != null)
TryReleaseTcpPort((ushort) port, portName);
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
ModeController.Start(server, mode);
}
public static async Task StopAsync()
{
if (Lock.CurrentCount == 0)
{
(await Lock.EnterAsync()).Dispose();
return;
}
using var _ = await Lock.EnterAsync();
if (ServerController == null && ModeController == null)
return;
Log.Information("Stop Main Controller");
StatusPortInfoText.Reset();
Task.Run(() => NTTController.Stop()).Forget();
var tasks = new[]
{
Task.Run(() => ServerController?.Stop()),
Task.Run(() => ModeController?.Stop())
Task.Run(() => ServerController?.StopAsync()),
Task.Run(() => ModeController?.StopAsync())
};
try
@@ -115,11 +125,12 @@ namespace Netch.Controllers
}
catch (Exception e)
{
Log.Error(e, "主控制器停止未处理异常");
Log.Error(e, "MainController Stop Error");
}
ModeController = null;
ServerController = null;
ModeController = null;
ModeFeatures = 0;
}
public static void PortCheck(ushort port, string portName, PortType portType = PortType.Both)
@@ -159,5 +170,30 @@ namespace Netch.Controllers
PortCheck(port, portName, PortType.TCP);
}
public static async Task<NatTypeTestResult> DiscoveryNatTypeAsync(CancellationToken ctx = default)
{
Debug.Assert(Socks5Server != null, nameof(Socks5Server) + " != null");
return await Socks5ServerTestUtils.DiscoveryNatTypeAsync(Socks5Server, ctx);
}
public static async Task<int?> HttpConnectAsync(CancellationToken ctx = default)
{
Debug.Assert(Socks5Server != null, nameof(Socks5Server) + " != null");
try
{
return await Socks5ServerTestUtils.HttpConnectAsync(Socks5Server, ctx);
}
catch (OperationCanceledException)
{
// ignored
}
catch (Exception e)
{
Log.Warning(e, "Unhandled Socks5ServerTestUtils.HttpConnectAsync Exception");
}
return null;
}
}
}

View File

@@ -1,14 +1,13 @@
using System;
using System.Collections.Generic;
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;
using Netch.Servers;
using Netch.Servers.Shadowsocks;
using Netch.Utils;
using Serilog;
using static Netch.Interops.Redirector;
@@ -27,37 +26,47 @@ namespace Netch.Controllers
public string Name => "Redirector";
public void Start(Server server, Mode mode)
public async Task StartAsync(Socks5Server server, Mode mode)
{
_server = server;
_mode = mode;
_rdrConfig = Global.Settings.Redirector;
CheckDriver();
Dial(NameList.TYPE_FILTERLOOPBACK, "false");
Dial(NameList.TYPE_FILTERICMP, "true");
var p = PortHelper.GetAvailablePort();
Dial(NameList.TYPE_TCPLISN, p.ToString());
Dial(NameList.TYPE_UDPLISN, p.ToString());
Dial(NameList.AIO_FILTERLOOPBACK, "false");
Dial(NameList.AIO_FILTERINTRANET, "true");
Dial(NameList.AIO_FILTERPARENT, _rdrConfig.ChildProcessHandle.ToString().ToLower());
Dial(NameList.AIO_FILTERICMP, _rdrConfig.FilterICMP.ToString().ToLower());
Dial(NameList.AIO_ICMPING, _rdrConfig.ICMPDelay.ToString());
// 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);
Dial(NameList.AIO_FILTERUDP, _rdrConfig.FilterProtocol.HasFlag(PortType.UDP).ToString().ToLower());
Dial(NameList.AIO_FILTERTCP, _rdrConfig.FilterProtocol.HasFlag(PortType.TCP).ToString().ToLower());
Dial(NameList.AIO_TGTHOST, await server.AutoResolveHostnameAsync());
Dial(NameList.AIO_TGTPORT, server.Port.ToString());
Dial(NameList.AIO_TGTUSER, server.Username ?? string.Empty);
Dial(NameList.AIO_TGTPASS, server.Password ?? string.Empty);
// Mode Rule
dial_Name(_mode);
DialRule(_mode);
// Features
Dial(NameList.TYPE_DNSHOST, _rdrConfig.DNSHijack ? _rdrConfig.DNSHijackHost : "");
// DNS
Dial(NameList.AIO_FILTERDNS, _rdrConfig.DNSHijack.ToString().ToLower());
if (_rdrConfig.DNSHijack)
{
var dns = new Uri(DnsUtils.AppendScheme(DnsUtils.AppendPort(_rdrConfig.DNSHijackHost), "udp"));
Dial(NameList.AIO_DNSHOST, dns.Host);
Dial(NameList.AIO_DNSPORT, dns.Port.ToString());
}
if (!Init())
if (!await InitAsync())
throw new MessageException("Redirector start failed.");
}
public void Stop()
public async Task StopAsync()
{
Free();
await FreeAsync();
}
#region CheckRule
@@ -72,14 +81,14 @@ namespace Netch.Controllers
try
{
if (r.StartsWith("!"))
return Dial(NameList.TYPE_ADDNAME, r.Substring(1));
return Dial(NameList.AIO_ADDNAME, r.Substring(1));
return Dial(NameList.TYPE_ADDNAME, r);
return Dial(NameList.AIO_ADDNAME, r);
}
finally
{
if (clear)
Dial(NameList.TYPE_CLRNAME, "");
Dial(NameList.AIO_CLRNAME, "");
}
}
@@ -91,72 +100,40 @@ namespace Netch.Controllers
public static bool CheckRules(IEnumerable<string> rules, out IEnumerable<string> results)
{
results = rules.Where(r => !CheckCppRegex(r, false));
Dial(NameList.TYPE_CLRNAME, "");
Dial(NameList.AIO_CLRNAME, "");
return !results.Any();
}
public static string GenerateInvalidRulesMessage(IEnumerable<string> rules)
{
return $"{string.Join("\n", rules)}\nAbove rules does not conform to C++ regular expression syntax";
return $"{string.Join("\n", rules)}\n" + i18N.Translate("Above rules does not conform to C++ regular expression syntax");
}
#endregion
private void dial_Server(PortType portType, in Server server)
private void DialRule(Mode mode)
{
if (portType == PortType.Both)
{
dial_Server(PortType.TCP, server);
dial_Server(PortType.UDP, server);
return;
}
var offset = portType == PortType.UDP ? UdpNameListOffset : 0;
if (server is Socks5 socks5)
{
Dial(NameList.TYPE_TCPTYPE + offset, "Socks5");
Dial(NameList.TYPE_TCPHOST + offset, $"{socks5.AutoResolveHostname()}:{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);
}
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_TCPMETH + offset, shadowsocks.EncryptMethod);
Dial(NameList.TYPE_TCPPASS + offset, shadowsocks.Password);
}
else
{
Trace.Assert(false);
}
}
private void dial_Name(Mode mode)
{
Dial(NameList.TYPE_CLRNAME, "");
Dial(NameList.AIO_CLRNAME, "");
var invalidList = new List<string>();
foreach (var s in mode.GetRules())
{
if (s.StartsWith("!"))
{
if (!Dial(NameList.TYPE_BYPNAME, s.Substring(1)))
if (!Dial(NameList.AIO_BYPNAME, s.Substring(1)))
invalidList.Add(s);
continue;
}
if (!Dial(NameList.TYPE_ADDNAME, s))
if (!Dial(NameList.AIO_ADDNAME, s))
invalidList.Add(s);
}
if (invalidList.Any())
throw new MessageException(GenerateInvalidRulesMessage(invalidList));
Dial(NameList.TYPE_ADDNAME, @"NTT\.exe");
Dial(NameList.TYPE_BYPNAME, "^" + Global.NetchDir.ToRegexString() + @"((?!NTT\.exe).)*$");
// Bypass Self
Dial(NameList.AIO_BYPNAME, "^" + Global.NetchDir.ToRegexString());
}
#region DriverUtil
@@ -166,8 +143,8 @@ namespace Netch.Controllers
var binFileVersion = Utils.Utils.GetFileVersion(Constants.NFDriver);
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
Log.Information("内置驱动版本: {Name}", binFileVersion);
Log.Information("系统驱动版本: {Name}", systemFileVersion);
Log.Information("Built-in netfilter2 driver version: {Name}", binFileVersion);
Log.Information("Installed netfilter2 driver version: {Name}", systemFileVersion);
if (!File.Exists(SystemDriver))
{
@@ -197,7 +174,7 @@ namespace Netch.Controllers
if (!reinstall)
return;
Log.Information("更新驱动");
Log.Information("Update netfilter2 driver");
UninstallDriver();
InstallDriver();
}
@@ -208,7 +185,8 @@ namespace Netch.Controllers
/// <returns>驱动是否安装成功</returns>
private static void InstallDriver()
{
Log.Information("安装 NF 驱动");
Log.Information("Install netfilter2 driver");
Global.MainForm.StatusText(i18N.Translate("Installing netfilter2 driver"));
if (!File.Exists(Constants.NFDriver))
throw new MessageException(i18N.Translate("builtin driver files missing, can't install NF driver"));
@@ -219,21 +197,20 @@ namespace Netch.Controllers
}
catch (Exception e)
{
Log.Error(e, "驱动复制失败\n");
throw new MessageException($"Copy NF driver file failed\n{e.Message}");
Log.Error(e, "Copy netfilter2.sys failed\n");
throw new MessageException($"Copy netfilter2.sys failed\n{e.Message}");
}
Global.MainForm.StatusText(i18N.Translate("Register driver"));
// 注册驱动文件
var result = NFAPI.nf_registerDriver("netfilter2");
if (result == NF_STATUS.NF_STATUS_SUCCESS)
{
Log.Information("驱动安装成功");
Log.Information("Install netfilter2 driver finished");
}
else
{
Log.Error("注册驱动失败: {Result}", result);
throw new MessageException($"Register NF driver failed\n{result}");
Log.Error("Register netfilter2 failed: {Result}", result);
throw new MessageException($"Register netfilter2 failed\n{result}");
}
}
@@ -243,7 +220,7 @@ namespace Netch.Controllers
/// <returns>是否成功卸载</returns>
public static bool UninstallDriver()
{
Log.Information("卸载 NF 驱动");
Log.Information("Uninstall netfilter2");
try
{
if (NFService.Status == ServiceControllerStatus.Running)

View File

@@ -1,101 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Netch.Interfaces;
using Netch.Utils;
using Serilog;
namespace Netch.Controllers
{
public class NTTController : Guard, IController
{
public NTTController() : base("NTT.exe")
{
}
public override string Name => "NTT";
/// <summary>
/// 启动 NatTypeTester
/// </summary>
/// <returns></returns>
public async Task<(string? result, string? localEnd, string? publicEnd)> Start()
{
string? localEnd = null, publicEnd = null, result = null, bindingTest = null;
try
{
Instance.StartInfo.Arguments = $" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}";
Instance.Start();
var output = await Instance.StandardOutput.ReadToEndAsync();
var error = await Instance.StandardError.ReadToEndAsync();
try
{
await File.WriteAllTextAsync(Path.Combine(Global.NetchDir, $"logging\\{Name}.log"), $"{output}\r\n{error}");
}
catch (Exception e)
{
Log.Warning(e, "写入 {Name} 日志错误", Name);
}
if (output.IsNullOrWhiteSpace())
if (!error.IsNullOrWhiteSpace())
{
var errorFirst = error.GetLines().First();
return (errorFirst.SplitTrimEntries(':').Last(), null, null);
}
foreach (var line in output.Split('\n'))
{
var str = line.SplitTrimEntries(':');
if (str.Length < 2)
continue;
var key = str[0];
var value = str[1];
switch (key)
{
case "Other address is":
case "Nat mapping behavior":
case "Nat filtering behavior":
break;
case "Binding test":
bindingTest = value;
break;
case "Local address":
localEnd = value;
break;
case "Mapped address":
publicEnd = value;
break;
case "result":
result = value;
break;
}
}
if (bindingTest == "Fail")
result = "Fail";
return (result, localEnd, publicEnd);
}
catch (Exception e)
{
Log.Error(e, "{Name} 控制器启动异常", Name);
try
{
Stop();
}
catch
{
// ignored
}
return (null, null, null);
}
}
}
}

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(Socks5Server 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 Socks5Server socks5 && !socks5.Auth())
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(Socks5Server server, Mode mode)
{
_mode = mode;
_tunConfig = Global.Settings.TUNTAP;
if (server is Socks5Bridge socks5Bridge)
_serverRemoteAddress = DnsUtils.Lookup(socks5Bridge.RemoteHostname);
if (server is Socks5LocalServer socks5Bridge)
_serverRemoteAddress = await DnsUtils.LookupAsync(socks5Bridge.RemoteHostname);
if (_serverRemoteAddress != null && IPAddress.IsLoopback(_serverRemoteAddress))
_serverRemoteAddress = null;
@@ -56,11 +56,11 @@ namespace Netch.Controllers
Dial(NameList.TYPE_UDPREST, "");
Dial(NameList.TYPE_UDPTYPE, "Socks5");
if (server is Socks5 socks5)
if (server is Socks5Server 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()
@@ -125,8 +125,8 @@ namespace Netch.Controllers
var binHash = Utils.Utils.SHA256CheckSum(binDriver);
var sysHash = Utils.Utils.SHA256CheckSum(sysDriver);
Log.Information("自带 wintun.dll Hash: {Hash}", binHash);
Log.Information("系统 wintun.dll Hash: {Hash}", sysHash);
Log.Information("Built-in wintun.dll Hash: {Hash}", binHash);
Log.Information("Installed wintun.dll Hash: {Hash}", sysHash);
if (binHash == sysHash)
return;
@@ -137,7 +137,7 @@ namespace Netch.Controllers
}
catch (Exception e)
{
Log.Error(e, "复制 wintun.dll 异常");
Log.Error(e, "Copy wintun.dll failed");
throw new MessageException($"Failed to copy wintun.dll to system directory: {e.Message}");
}
}
@@ -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.9.2";
private const string Suffix = @"";
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
@@ -37,37 +37,37 @@ 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);
Log.Information("Github 最新发布版本: {Version}", LatestRelease.tag_name);
Log.Information("Github latest release: {Version}", LatestRelease.tag_name);
if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0)
{
Log.Information("发现新版本");
NewVersionFound?.Invoke(null, new EventArgs());
Log.Information("Found newer version");
NewVersionFound?.Invoke(null, EventArgs.Empty);
}
else
{
Log.Information("目前是最新版本");
NewVersionNotFound?.Invoke(null, new EventArgs());
Log.Information("Already the latest version");
NewVersionNotFound?.Invoke(null, EventArgs.Empty);
}
}
catch (Exception e)
{
if (e is WebException)
Log.Warning(e, "获取新版本失败");
Log.Warning(e, "Get releases failed");
else
Log.Error(e, "获取新版本异常");
Log.Error(e, "Get releases error");
NewVersionFoundFailed?.Invoke(null, new EventArgs());
NewVersionFoundFailed?.Invoke(null, EventArgs.Empty);
}
}

View File

@@ -1,4 +1,4 @@
namespace Netch.Models
namespace Netch.Enums
{
public enum LogLevel
{

View File

@@ -0,0 +1,13 @@
using System;
namespace Netch.Enums
{
[Flags]
public enum ModeFeature
{
SupportSocks5 = 0,
SupportIPv4 = 0,
SupportSocks5Auth = 0b_0001,
SupportIPv6 = 0b_0100
}
}

View File

@@ -1,4 +1,4 @@
namespace Netch.Models
namespace Netch.Enums
{
/// <summary>
/// 状态

View File

@@ -7,5 +7,7 @@ namespace Netch
public static readonly bool IsWindows10Upper = Environment.OSVersion.Version.Major >= 10;
public static bool AlwaysShowNewVersionFound { get; set; }
public static bool NoSupport { get; set; }
}
}

View File

@@ -1,7 +1,6 @@
using Netch.Properties;
using Netch.Utils;
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace Netch.Forms

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

@@ -35,19 +35,19 @@
this.ModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CreateProcessModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CreateRouteTableRuleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SubscribeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ManageSubscribeLinksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UpdateServersFromSubscribeLinksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.SubscriptionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ManageSubscriptionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UpdateServersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.OptionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.OpenDirectoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ShowHideConsoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CleanDNSCacheToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UninstallServiceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.removeNetchFirewallRulesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.RemoveNetchFirewallRulesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.HelpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CheckForUpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fAQToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CheckForUpdateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.FAQToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ForceExitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.AboutToolStripButton = new System.Windows.Forms.ToolStripButton();
this.NewVersionLabel = new System.Windows.Forms.ToolStripLabel();
this.VersionLabel = new System.Windows.Forms.ToolStripLabel();
@@ -73,6 +73,7 @@
this.DownloadSpeedLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.UploadSpeedLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.blankToolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.HttpStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.NatTypeStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.NatTypeStatusLightLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.ControlButton = new System.Windows.Forms.Button();
@@ -110,10 +111,10 @@
this.MenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ServerToolStripMenuItem,
this.ModeToolStripMenuItem,
this.SubscribeToolStripMenuItem,
this.SubscriptionToolStripMenuItem,
this.OptionsToolStripMenuItem,
this.HelpToolStripMenuItem,
this.exitToolStripMenuItem,
this.ForceExitToolStripMenuItem,
this.AboutToolStripButton,
this.NewVersionLabel,
this.VersionLabel});
@@ -163,29 +164,29 @@
this.CreateRouteTableRuleToolStripMenuItem.Text = "Create Route Table Rule";
this.CreateRouteTableRuleToolStripMenuItem.Click += new System.EventHandler(this.createRouteTableModeToolStripMenuItem_Click);
//
// SubscribeToolStripMenuItem
// SubscriptionToolStripMenuItem
//
this.SubscribeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ManageSubscribeLinksToolStripMenuItem,
this.UpdateServersFromSubscribeLinksToolStripMenuItem});
this.SubscribeToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.SubscribeToolStripMenuItem.Name = "SubscribeToolStripMenuItem";
this.SubscribeToolStripMenuItem.Size = new System.Drawing.Size(77, 21);
this.SubscribeToolStripMenuItem.Text = "Subscribe";
this.SubscriptionToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ManageSubscriptionsToolStripMenuItem,
this.UpdateServersToolStripMenuItem});
this.SubscriptionToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.SubscriptionToolStripMenuItem.Name = "SubscriptionToolStripMenuItem";
this.SubscriptionToolStripMenuItem.Size = new System.Drawing.Size(92, 21);
this.SubscriptionToolStripMenuItem.Text = "Subscription";
//
// ManageSubscribeLinksToolStripMenuItem
// ManageSubscriptionsToolStripMenuItem
//
this.ManageSubscribeLinksToolStripMenuItem.Name = "ManageSubscribeLinksToolStripMenuItem";
this.ManageSubscribeLinksToolStripMenuItem.Size = new System.Drawing.Size(294, 22);
this.ManageSubscribeLinksToolStripMenuItem.Text = "Manage Subscribe Links";
this.ManageSubscribeLinksToolStripMenuItem.Click += new System.EventHandler(this.ManageSubscribeLinksToolStripMenuItem_Click);
this.ManageSubscriptionsToolStripMenuItem.Name = "ManageSubscriptionsToolStripMenuItem";
this.ManageSubscriptionsToolStripMenuItem.Size = new System.Drawing.Size(206, 22);
this.ManageSubscriptionsToolStripMenuItem.Text = "Manage Subscriptions";
this.ManageSubscriptionsToolStripMenuItem.Click += new System.EventHandler(this.ManageSubscriptionLinksToolStripMenuItem_Click);
//
// UpdateServersFromSubscribeLinksToolStripMenuItem
// UpdateServersToolStripMenuItem
//
this.UpdateServersFromSubscribeLinksToolStripMenuItem.Name = "UpdateServersFromSubscribeLinksToolStripMenuItem";
this.UpdateServersFromSubscribeLinksToolStripMenuItem.Size = new System.Drawing.Size(294, 22);
this.UpdateServersFromSubscribeLinksToolStripMenuItem.Text = "Update Servers From Subscribe Links";
this.UpdateServersFromSubscribeLinksToolStripMenuItem.Click += new System.EventHandler(this.UpdateServersFromSubscribeLinksToolStripMenuItem_Click);
this.UpdateServersToolStripMenuItem.Name = "UpdateServersToolStripMenuItem";
this.UpdateServersToolStripMenuItem.Size = new System.Drawing.Size(206, 22);
this.UpdateServersToolStripMenuItem.Text = "Update Servers";
this.UpdateServersToolStripMenuItem.Click += new System.EventHandler(this.UpdateServersFromSubscriptionLinksToolStripMenuItem_Click);
//
// OptionsToolStripMenuItem
//
@@ -194,7 +195,7 @@
this.ShowHideConsoleToolStripMenuItem,
this.CleanDNSCacheToolStripMenuItem,
this.UninstallServiceToolStripMenuItem,
this.removeNetchFirewallRulesToolStripMenuItem});
this.RemoveNetchFirewallRulesToolStripMenuItem});
this.OptionsToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.OptionsToolStripMenuItem.Name = "OptionsToolStripMenuItem";
this.OptionsToolStripMenuItem.Size = new System.Drawing.Size(66, 21);
@@ -228,43 +229,43 @@
this.UninstallServiceToolStripMenuItem.Text = "Uninstall NF Service";
this.UninstallServiceToolStripMenuItem.Click += new System.EventHandler(this.UninstallServiceToolStripMenuItem_Click);
//
// removeNetchFirewallRulesToolStripMenuItem
// RemoveNetchFirewallRulesToolStripMenuItem
//
this.removeNetchFirewallRulesToolStripMenuItem.Name = "removeNetchFirewallRulesToolStripMenuItem";
this.removeNetchFirewallRulesToolStripMenuItem.Size = new System.Drawing.Size(243, 22);
this.removeNetchFirewallRulesToolStripMenuItem.Text = "Remove Netch Firewall Rules";
this.removeNetchFirewallRulesToolStripMenuItem.Click += new System.EventHandler(this.RemoveNetchFirewallRulesToolStripMenuItem_Click);
this.RemoveNetchFirewallRulesToolStripMenuItem.Name = "RemoveNetchFirewallRulesToolStripMenuItem";
this.RemoveNetchFirewallRulesToolStripMenuItem.Size = new System.Drawing.Size(243, 22);
this.RemoveNetchFirewallRulesToolStripMenuItem.Text = "Remove Netch Firewall Rules";
this.RemoveNetchFirewallRulesToolStripMenuItem.Click += new System.EventHandler(this.RemoveNetchFirewallRulesToolStripMenuItem_Click);
//
// HelpToolStripMenuItem
//
this.HelpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.CheckForUpdatesToolStripMenuItem,
this.fAQToolStripMenuItem});
this.CheckForUpdateToolStripMenuItem,
this.FAQToolStripMenuItem});
this.HelpToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.HelpToolStripMenuItem.Name = "HelpToolStripMenuItem";
this.HelpToolStripMenuItem.Size = new System.Drawing.Size(47, 21);
this.HelpToolStripMenuItem.Text = "Help";
//
// CheckForUpdatesToolStripMenuItem
// CheckForUpdateToolStripMenuItem
//
this.CheckForUpdatesToolStripMenuItem.Name = "CheckForUpdatesToolStripMenuItem";
this.CheckForUpdatesToolStripMenuItem.Size = new System.Drawing.Size(183, 22);
this.CheckForUpdatesToolStripMenuItem.Text = "Check for updates";
this.CheckForUpdatesToolStripMenuItem.Click += new System.EventHandler(this.CheckForUpdatesToolStripMenuItem_Click);
this.CheckForUpdateToolStripMenuItem.Name = "CheckForUpdateToolStripMenuItem";
this.CheckForUpdateToolStripMenuItem.Size = new System.Drawing.Size(177, 22);
this.CheckForUpdateToolStripMenuItem.Text = "Check for update";
this.CheckForUpdateToolStripMenuItem.Click += new System.EventHandler(this.CheckForUpdatesToolStripMenuItem_Click);
//
// fAQToolStripMenuItem
// FAQToolStripMenuItem
//
this.fAQToolStripMenuItem.Name = "fAQToolStripMenuItem";
this.fAQToolStripMenuItem.Size = new System.Drawing.Size(183, 22);
this.fAQToolStripMenuItem.Text = "FAQ";
this.fAQToolStripMenuItem.Click += new System.EventHandler(this.fAQToolStripMenuItem_Click);
this.FAQToolStripMenuItem.Name = "FAQToolStripMenuItem";
this.FAQToolStripMenuItem.Size = new System.Drawing.Size(177, 22);
this.FAQToolStripMenuItem.Text = "FAQ";
this.FAQToolStripMenuItem.Click += new System.EventHandler(this.fAQToolStripMenuItem_Click);
//
// exitToolStripMenuItem
// ForceExitToolStripMenuItem
//
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
this.exitToolStripMenuItem.Size = new System.Drawing.Size(40, 22);
this.exitToolStripMenuItem.Text = "Exit";
this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
this.ForceExitToolStripMenuItem.Name = "ForceExitToolStripMenuItem";
this.ForceExitToolStripMenuItem.Size = new System.Drawing.Size(40, 22);
this.ForceExitToolStripMenuItem.Text = "Exit";
this.ForceExitToolStripMenuItem.Click += new System.EventHandler(this.ForceExitToolStripMenuItem_Click);
//
// AboutToolStripButton
//
@@ -521,6 +522,7 @@
this.DownloadSpeedLabel,
this.UploadSpeedLabel,
this.blankToolStripStatusLabel,
this.HttpStatusLabel,
this.NatTypeStatusLabel,
this.NatTypeStatusLightLabel});
this.StatusStrip.Location = new System.Drawing.Point(0, 272);
@@ -563,6 +565,15 @@
this.blankToolStripStatusLabel.Size = new System.Drawing.Size(494, 17);
this.blankToolStripStatusLabel.Spring = true;
//
// HttpStatusLabel
//
this.HttpStatusLabel.Name = "HttpStatusLabel";
this.HttpStatusLabel.Size = new System.Drawing.Size(41, 17);
this.HttpStatusLabel.Text = "HTTP:";
this.HttpStatusLabel.TextAlign = System.Drawing.ContentAlignment.BottomLeft;
this.HttpStatusLabel.Visible = false;
this.HttpStatusLabel.Click += new System.EventHandler(this.TcpStatusLabel_Click);
//
// NatTypeStatusLabel
//
this.NatTypeStatusLabel.Name = "NatTypeStatusLabel";
@@ -733,7 +744,7 @@
private System.Windows.Forms.ToolStripMenuItem CreateRouteTableRuleToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem removeNetchFirewallRulesToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem RemoveNetchFirewallRulesToolStripMenuItem;
private System.Windows.Forms.ToolStripButton AboutToolStripButton;
private System.Windows.Forms.ToolStripMenuItem CleanDNSCacheToolStripMenuItem;
@@ -748,9 +759,9 @@
private System.Windows.Forms.PictureBox EditModePictureBox;
private System.Windows.Forms.PictureBox EditServerPictureBox;
private System.Windows.Forms.ToolStripMenuItem ExitToolStripButton;
private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ForceExitToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ImportServersFromClipboardToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ManageSubscribeLinksToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem ManageSubscriptionsToolStripMenuItem;
private System.Windows.Forms.MenuStrip MenuStrip;
public System.Windows.Forms.ComboBox ModeComboBox;
private System.Windows.Forms.Label ModeLabel;
@@ -765,7 +776,7 @@
private System.Windows.Forms.Label ProfileLabel;
private System.Windows.Forms.TextBox ProfileNameText;
private System.Windows.Forms.TableLayoutPanel ProfileTable;
private System.Windows.Forms.ToolStripMenuItem CheckForUpdatesToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem CheckForUpdateToolStripMenuItem;
private System.Windows.Forms.ComboBox ServerComboBox;
private System.Windows.Forms.Label ServerLabel;
private System.Windows.Forms.ToolStripMenuItem ServerToolStripMenuItem;
@@ -774,23 +785,24 @@
private System.Windows.Forms.PictureBox SpeedPictureBox;
private System.Windows.Forms.ToolStripStatusLabel StatusLabel;
private System.Windows.Forms.StatusStrip StatusStrip;
private System.Windows.Forms.ToolStripMenuItem SubscribeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem SubscriptionToolStripMenuItem;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3;
private System.Windows.Forms.ToolStripMenuItem UninstallServiceToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem UpdateServersFromSubscribeLinksToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem UpdateServersToolStripMenuItem;
private System.Windows.Forms.ToolStripStatusLabel UploadSpeedLabel;
private System.Windows.Forms.ToolStripStatusLabel UsedBandwidthLabel;
private System.Windows.Forms.ToolStripLabel NewVersionLabel;
private System.Windows.Forms.ToolStripLabel VersionLabel;
private System.Windows.Forms.ToolStripStatusLabel NatTypeStatusLightLabel;
private System.Windows.Forms.ToolStripStatusLabel blankToolStripStatusLabel;
private System.Windows.Forms.ToolStripMenuItem fAQToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem FAQToolStripMenuItem;
#endregion
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
private System.Windows.Forms.ContainerControl ButtomControlContainerControl;
private System.Windows.Forms.ToolStripMenuItem ShowHideConsoleToolStripMenuItem;
private System.Windows.Forms.ToolStripStatusLabel HttpStatusLabel;
}
}

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
{
@@ -40,6 +43,9 @@ namespace Netch.Forms
#region i18N Translations
if (Flags.NoSupport)
_mainFormText.Add(Name, new[] { "{0} ({1})", "Netch", "No Support" });
_mainFormText.Add(UninstallServiceToolStripMenuItem.Name, new[] { "Uninstall {0}", "NF Service" });
#endregion
@@ -50,7 +56,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
@@ -74,7 +81,7 @@ namespace Netch.Forms
LoadServers();
SelectLastServer();
ServerHelper.DelayTestHelper.UpdateInterval();
DelayTestHelper.UpdateTick(true);
ModeHelper.InitWatcher();
ModeHelper.Load();
@@ -84,29 +91,23 @@ namespace Netch.Forms
// 加载翻译
TranslateControls();
// 隐藏 NatTypeStatusLabel
NatTypeStatusText();
// 隐藏 ConnectivityStatusLabel
ConnectivityStatusVisible(false);
// 加载快速配置
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)
UpdateServersFromSubscriptionAsync().Forget();
// 打开软件时启动加速,产生开始按钮点击事件
if (Global.Settings.StartWhenOpened)
ControlButton_Click(null, null);
}));
// 打开软件时启动加速,产生开始按钮点击事件
if (Global.Settings.StartWhenOpened)
ControlButton.PerformClick();
Netch.SingleInstance.ListenForArgumentsFromSuccessiveInstances();
}
@@ -214,15 +215,18 @@ namespace Netch.Forms
private async void ImportServersFromClipboardToolStripMenuItem_Click(object sender, EventArgs e)
{
var texts = Clipboard.GetText();
if (!string.IsNullOrWhiteSpace(texts))
{
var servers = ShareLink.ParseText(texts);
Global.Settings.Server.AddRange(servers);
NotifyTip(i18N.TranslateFormat("Import {0} server(s) form Clipboard", servers.Count));
if (string.IsNullOrWhiteSpace(texts))
return;
LoadServers();
await Configuration.SaveAsync();
}
var servers = ShareLink.ParseText(texts);
foreach (var server in servers)
server.Group = Constants.DefaultGroup;
Global.Settings.Server.AddRange(servers);
NotifyTip(i18N.TranslateFormat("Import {0} server(s) form Clipboard", servers.Count));
LoadServers();
await Configuration.SaveAsync();
}
private async void AddServerToolStripMenuItem_Click([NotNull] object? sender, EventArgs? e)
@@ -262,47 +266,47 @@ namespace Netch.Forms
#region Subscription
private void ManageSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
private void ManageSubscriptionLinksToolStripMenuItem_Click(object sender, EventArgs e)
{
Hide();
new SubscribeForm().ShowDialog();
new SubscriptionForm().ShowDialog();
LoadServers();
Show();
}
private async void UpdateServersFromSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
private async void UpdateServersFromSubscriptionLinksToolStripMenuItem_Click(object sender, EventArgs e)
{
await UpdateServersFromSubscribe();
await UpdateServersFromSubscriptionAsync();
}
private async Task UpdateServersFromSubscribe()
private async Task UpdateServersFromSubscriptionAsync()
{
void DisableItems(bool v)
{
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ProfileGroupBox.Enabled = ControlButton.Enabled = v;
}
if (Global.Settings.SubscribeLink.Count <= 0)
if (Global.Settings.Subscription.Count <= 0)
{
MessageBoxX.Show(i18N.Translate("No subscription link"));
return;
}
StatusText(i18N.Translate("Starting update subscription"));
StatusText(i18N.Translate("Updating servers"));
DisableItems(false);
try
{
await Subscription.UpdateServersAsync();
await SubscriptionUtil.UpdateServersAsync();
LoadServers();
await Configuration.SaveAsync();
StatusText(i18N.Translate("Subscription updated"));
StatusText(i18N.Translate("Servers updated"));
}
catch (Exception e)
{
NotifyTip(i18N.Translate("update servers failed") + "\n" + e.Message, info: false);
Log.Error("更新服务器 失败!" + e);
NotifyTip(i18N.Translate("Unhandled update servers error") + "\n" + e.Message, info: false);
Log.Error(e, "Unhandled Update servers error");
}
finally
{
@@ -323,14 +327,14 @@ namespace Netch.Forms
void OnNewVersionFoundFailed(object? o, EventArgs? args)
{
NotifyTip(i18N.Translate("New version found failed"), info: false);
NotifyTip(i18N.Translate("Check for update failed"), info: false);
}
try
{
UpdateChecker.NewVersionNotFound += OnNewVersionNotFound;
UpdateChecker.NewVersionFoundFailed += OnNewVersionFoundFailed;
await CheckUpdate();
await CheckUpdateAsync();
}
finally
{
@@ -391,9 +395,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
@@ -401,7 +405,7 @@ namespace Netch.Forms
/// <summary>
/// 菜单栏强制退出
/// </summary>
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
private void ForceExitToolStripMenuItem_Click(object sender, EventArgs e)
{
Exit(true);
}
@@ -464,7 +468,7 @@ namespace Netch.Forms
}
ModeHelper.SuspendWatcher = true;
await Stop();
await StopAsync();
await Configuration.SaveAsync();
// Update
@@ -481,7 +485,7 @@ namespace Netch.Forms
}
catch (Exception exception)
{
Log.Error(exception, "更新未处理异常");
Log.Error(exception, "Unhandled Update error");
NotifyTip(exception.Message, info: false);
}
finally
@@ -500,7 +504,7 @@ namespace Netch.Forms
private void fAQToolStripMenuItem_Click(object sender, EventArgs e)
{
Utils.Utils.Open("https://netch.org/#/docs/zh-CN/faq");
Utils.Utils.Open("https://docs.netch.org");
}
#endregion
@@ -511,11 +515,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 +551,32 @@ namespace Netch.Forms
State = State.Started;
Task.Run(Bandwidth.NetTraffic).Forget();
Task.Run(NatTest).Forget();
DiscoveryNatTypeAsync().Forget();
HttpConnectAsync().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
@@ -577,7 +585,7 @@ namespace Netch.Forms
private void SettingsButton_Click(object sender, EventArgs e)
{
var oldSettings = Global.Settings.Clone();
var oldSettings = Global.Settings.ShallowCopy();
Hide();
new SettingForm().ShowDialog();
@@ -591,7 +599,7 @@ namespace Netch.Forms
}
if (oldSettings.DetectionTick != Global.Settings.DetectionTick)
ServerHelper.DelayTestHelper.UpdateInterval();
DelayTestHelper.UpdateTick(true);
if (oldSettings.ProfileCount != Global.Settings.ProfileCount)
LoadProfiles();
@@ -636,9 +644,6 @@ namespace Netch.Forms
return;
}
if (!server.Valid())
return;
Hide();
ServerHelper.GetUtilByTypeName(server.Type).Edit(server);
LoadServers();
@@ -646,7 +651,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 +665,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 DelayTestHelper.PerformTestAsync(true);
Enable();
}
}
@@ -685,9 +684,6 @@ namespace Netch.Forms
return;
}
if (!server.Valid())
return;
try
{
//听说巨硬BUG经常会炸所以Catch一下 :D
@@ -1001,12 +997,13 @@ namespace Netch.Forms
EditServerPictureBox.Enabled = DeleteModePictureBox.Enabled = DeleteServerPictureBox.Enabled = enabled;
// 启动需要禁用的控件
UninstallServiceToolStripMenuItem.Enabled = UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled = enabled;
ServerToolStripMenuItem.Enabled = ModeToolStripMenuItem.Enabled =
SubscriptionToolStripMenuItem.Enabled = UninstallServiceToolStripMenuItem.Enabled = enabled;
}
_state = value;
ServerHelper.DelayTestHelper.Enabled = IsWaiting(_state);
DelayTestHelper.Enabled = IsWaiting(_state);
StatusText();
switch (value)
@@ -1036,7 +1033,7 @@ namespace Netch.Forms
ProfileGroupBox.Enabled = false;
BandwidthState(false);
NatTypeStatusText();
ConnectivityStatusVisible(false);
break;
case State.Stopped:
ControlButton.Enabled = true;
@@ -1053,29 +1050,28 @@ 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;
_discoveryNatCts?.Cancel();
_httpConnectCts?.Cancel();
await MainController.StopAsync();
State = State.Stopped;
}
private bool IsWaiting()
{
return State == State.Waiting || State == State.Stopped;
}
private bool IsWaiting() => IsWaiting(_state);
private static bool IsWaiting(State state)
{
return state == State.Waiting || state == State.Stopped;
return state is State.Waiting or State.Stopped;
}
/// <summary>
@@ -1091,9 +1087,10 @@ namespace Netch.Forms
}
text ??= i18N.Translate(StateExtension.GetStatusString(State));
StatusLabel.Text = i18N.Translate("Status", ": ") + text;
if (_state == State.Started)
StatusLabel.Text += StatusPortInfoText.Value;
text += StatusPortInfoText.Value;
StatusLabel.Text = i18N.Translate("Status", ": ") + text;
}
public void BandwidthState(bool state)
@@ -1110,24 +1107,14 @@ namespace Netch.Forms
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = state;
}
public void NatTypeStatusText(string? text = null, string? country = null)
private void UpdateNatTypeStatusLabelText(string? text, string? country = null)
{
if (InvokeRequired)
{
BeginInvoke(new Action<string, string>(NatTypeStatusText), text, country);
return;
}
if (State != State.Started)
{
NatTypeStatusLabel.Text = "";
NatTypeStatusLabel.Visible = NatTypeStatusLightLabel.Visible = false;
return;
}
if (!string.IsNullOrEmpty(text))
{
NatTypeStatusLabel.Text = $"NAT{i18N.Translate(": ")}{text} {(!country.IsNullOrEmpty() ? $"[{country}]" : "")}";
if (country == null)
NatTypeStatusLabel.Text = $"NAT{i18N.Translate(": ")}{text} ";
else
NatTypeStatusLabel.Text = $"NAT{i18N.Translate(": ")}{text} [{country}]";
UpdateNatTypeLight(int.TryParse(text, out var natType) ? natType : -1);
}
@@ -1139,10 +1126,18 @@ namespace Netch.Forms
NatTypeStatusLabel.Visible = true;
}
private void ConnectivityStatusVisible(bool visible)
{
if (!visible)
HttpStatusLabel.Text = NatTypeStatusLabel.Text = "";
HttpStatusLabel.Visible = NatTypeStatusLabel.Visible = NatTypeStatusLightLabel.Visible = visible;
}
/// <summary>
/// 更新 NAT指示灯颜色
/// </summary>
/// <param name="natType"></param>
/// <param name="natType">NAT Type. keep default(-1) to Hide Light</param>
private void UpdateNatTypeLight(int natType = -1)
{
if (natType > 0 && natType < 5)
@@ -1165,46 +1160,81 @@ namespace Netch.Forms
}
}
private async void NatTypeStatusLabel_Click(object sender, EventArgs e)
private async void TcpStatusLabel_Click(object sender, EventArgs e)
{
if (_state == State.Started && !Monitor.IsEntered(_natTestLock))
await NatTest();
await HttpConnectAsync();
}
private bool _natTestLock = true;
/// <summary>
/// 测试 NAT
/// </summary>
private async Task NatTest()
private async void NatTypeStatusLabel_Click(object sender, EventArgs e)
{
if (!MainController.Mode!.TestNatRequired())
return;
await DiscoveryNatTypeAsync();
}
if (!_natTestLock)
return;
private CancellationTokenSource? _discoveryNatCts;
_natTestLock = false;
private async Task DiscoveryNatTypeAsync()
{
NatTypeStatusLabel.Enabled = false;
UpdateNatTypeStatusLabelText(i18N.Translate("Testing NAT Type"));
_discoveryNatCts = new CancellationTokenSource();
try
{
NatTypeStatusText(i18N.Translate("Testing NAT"));
var res = await MainController.DiscoveryNatTypeAsync(_discoveryNatCts.Token);
if (_discoveryNatCts.IsCancellationRequested)
return;
var (result, _, publicEnd) = await MainController.NTTController.Start();
if (!string.IsNullOrEmpty(publicEnd))
if (!string.IsNullOrEmpty(res.PublicEnd))
{
var country = Utils.Utils.GetCityCode(publicEnd!);
NatTypeStatusText(result, country);
var country = await Utils.Utils.GetCityCodeAsync(res.PublicEnd);
UpdateNatTypeStatusLabelText(res.Result, country);
if (int.TryParse(res.Result, out var natType))
UpdateNatTypeLight(natType);
else
UpdateNatTypeLight();
}
else
{
NatTypeStatusText(result ?? "Error");
UpdateNatTypeStatusLabelText(res.Result ?? "Error");
NatTypeStatusLightLabel.Visible = false;
}
}
finally
{
_natTestLock = true;
_discoveryNatCts.Dispose();
_discoveryNatCts = null;
NatTypeStatusLabel.Enabled = true;
}
}
private CancellationTokenSource? _httpConnectCts;
private async Task HttpConnectAsync()
{
HttpStatusLabel.Enabled = false;
_httpConnectCts = new CancellationTokenSource();
try
{
var res = await MainController.HttpConnectAsync(_httpConnectCts.Token);
if (_httpConnectCts.IsCancellationRequested)
return;
if (res != null)
HttpStatusLabel.Text = $"HTTP{i18N.Translate(": ")}{res}ms";
else
HttpStatusLabel.Text = $"HTTP{i18N.Translate(": ", "Timeout")}";
HttpStatusLabel.Visible = true;
}
finally
{
_httpConnectCts.Dispose();
_httpConnectCts = null;
HttpStatusLabel.Enabled = true;
}
}
@@ -1226,7 +1256,7 @@ namespace Netch.Forms
if (!IsWaiting())
{
_resumeFlag = true;
Log.Information("操作系统即将挂起,自动停止");
Log.Information("OS Suspend, Stop");
ControlButton_Click(null, null);
}
@@ -1235,7 +1265,7 @@ namespace Netch.Forms
if (_resumeFlag)
{
_resumeFlag = false;
Log.Information("操作系统即将从挂起状态继续,自动重启");
Log.Information("OS Resume, Restart");
ControlButton_Click(null, null);
}
@@ -1270,7 +1300,7 @@ namespace Netch.Forms
return;
}
State = State.Terminating;
// State = State.Terminating;
NotifyIcon.Visible = false;
Hide();
@@ -1281,7 +1311,7 @@ namespace Netch.Forms
if (File.Exists(file))
File.Delete(file);
await Stop();
await StopAsync();
Dispose();
Environment.Exit(Environment.ExitCode);
@@ -1311,12 +1341,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

@@ -1,7 +1,7 @@
using Netch.Models;
using Netch.Utils;
using Netch.Utils;
using System;
using System.Windows.Forms;
using Netch.Enums;
namespace Netch.Forms
{

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

@@ -36,10 +36,7 @@ namespace Netch.Forms
this.PortGroupBox = new System.Windows.Forms.GroupBox();
this.Socks5PortLabel = new System.Windows.Forms.Label();
this.Socks5PortTextBox = new System.Windows.Forms.TextBox();
this.HTTPPortLabel = new System.Windows.Forms.Label();
this.HTTPPortTextBox = new System.Windows.Forms.TextBox();
this.AllowDevicesCheckBox = new System.Windows.Forms.CheckBox();
this.ResolveServerHostnameCheckBox = new System.Windows.Forms.CheckBox();
this.ServerPingTypeLabel = new System.Windows.Forms.Label();
this.ICMPingRadioBtn = new System.Windows.Forms.RadioButton();
this.TCPingRadioBtn = new System.Windows.Forms.RadioButton();
@@ -56,12 +53,11 @@ namespace Netch.Forms
this.NFTabPage = new System.Windows.Forms.TabPage();
this.ProcessFilterProtocolLabel = new System.Windows.Forms.Label();
this.ProcessFilterProtocolComboBox = new System.Windows.Forms.ComboBox();
this.DNSHijackCheckBox = new System.Windows.Forms.CheckBox();
this.DNSHijackHostTextBox = new System.Windows.Forms.TextBox();
this.FilterICMPCheckBox = new System.Windows.Forms.CheckBox();
this.ICMPDelayLabel = new System.Windows.Forms.Label();
this.ICMPDelayTextBox = new System.Windows.Forms.TextBox();
this.RedirectorSSCheckBox = new System.Windows.Forms.CheckBox();
this.DNSHijackCheckBox = new System.Windows.Forms.CheckBox();
this.DNSHijackHostTextBox = new System.Windows.Forms.TextBox();
this.ChildProcessHandleCheckBox = new System.Windows.Forms.CheckBox();
this.WinTUNTabPage = new System.Windows.Forms.TabPage();
this.WinTUNGroupBox = new System.Windows.Forms.GroupBox();
@@ -101,6 +97,7 @@ namespace Netch.Forms
this.MinimizeWhenStartedCheckBox = new System.Windows.Forms.CheckBox();
this.RunAtStartupCheckBox = new System.Windows.Forms.CheckBox();
this.CheckUpdateWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
this.NoSupportDialogCheckBox = new System.Windows.Forms.CheckBox();
this.CheckBetaUpdateCheckBox = new System.Windows.Forms.CheckBox();
this.UpdateServersWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
this.AioDNSTabPage = new System.Windows.Forms.TabPage();
@@ -112,6 +109,7 @@ namespace Netch.Forms
this.AioDNSListenPortTextBox = new System.Windows.Forms.TextBox();
this.ControlButton = new System.Windows.Forms.Button();
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.DNSHijackLabel = new System.Windows.Forms.Label();
this.TabControl.SuspendLayout();
this.GeneralTabPage.SuspendLayout();
this.PortGroupBox.SuspendLayout();
@@ -144,7 +142,6 @@ namespace Netch.Forms
//
this.GeneralTabPage.BackColor = System.Drawing.SystemColors.ButtonFace;
this.GeneralTabPage.Controls.Add(this.PortGroupBox);
this.GeneralTabPage.Controls.Add(this.ResolveServerHostnameCheckBox);
this.GeneralTabPage.Controls.Add(this.ServerPingTypeLabel);
this.GeneralTabPage.Controls.Add(this.ICMPingRadioBtn);
this.GeneralTabPage.Controls.Add(this.TCPingRadioBtn);
@@ -169,8 +166,6 @@ namespace Netch.Forms
//
this.PortGroupBox.Controls.Add(this.Socks5PortLabel);
this.PortGroupBox.Controls.Add(this.Socks5PortTextBox);
this.PortGroupBox.Controls.Add(this.HTTPPortLabel);
this.PortGroupBox.Controls.Add(this.HTTPPortTextBox);
this.PortGroupBox.Controls.Add(this.AllowDevicesCheckBox);
this.PortGroupBox.Location = new System.Drawing.Point(8, 6);
this.PortGroupBox.Name = "PortGroupBox";
@@ -196,23 +191,6 @@ namespace Netch.Forms
this.Socks5PortTextBox.TabIndex = 1;
this.Socks5PortTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// HTTPPortLabel
//
this.HTTPPortLabel.AutoSize = true;
this.HTTPPortLabel.Location = new System.Drawing.Point(9, 54);
this.HTTPPortLabel.Name = "HTTPPortLabel";
this.HTTPPortLabel.Size = new System.Drawing.Size(38, 17);
this.HTTPPortLabel.TabIndex = 2;
this.HTTPPortLabel.Text = "HTTP";
//
// HTTPPortTextBox
//
this.HTTPPortTextBox.Location = new System.Drawing.Point(120, 51);
this.HTTPPortTextBox.Name = "HTTPPortTextBox";
this.HTTPPortTextBox.Size = new System.Drawing.Size(90, 23);
this.HTTPPortTextBox.TabIndex = 3;
this.HTTPPortTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// AllowDevicesCheckBox
//
this.AllowDevicesCheckBox.AutoSize = true;
@@ -224,20 +202,10 @@ namespace Netch.Forms
this.AllowDevicesCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.AllowDevicesCheckBox.UseVisualStyleBackColor = true;
//
// ResolveServerHostnameCheckBox
//
this.ResolveServerHostnameCheckBox.AutoSize = true;
this.ResolveServerHostnameCheckBox.Location = new System.Drawing.Point(267, 15);
this.ResolveServerHostnameCheckBox.Name = "ResolveServerHostnameCheckBox";
this.ResolveServerHostnameCheckBox.Size = new System.Drawing.Size(176, 21);
this.ResolveServerHostnameCheckBox.TabIndex = 1;
this.ResolveServerHostnameCheckBox.Text = "Resolve Server Hostname";
this.ResolveServerHostnameCheckBox.UseVisualStyleBackColor = true;
//
// ServerPingTypeLabel
//
this.ServerPingTypeLabel.AutoSize = true;
this.ServerPingTypeLabel.Location = new System.Drawing.Point(267, 44);
this.ServerPingTypeLabel.Location = new System.Drawing.Point(267, 15);
this.ServerPingTypeLabel.Name = "ServerPingTypeLabel";
this.ServerPingTypeLabel.Size = new System.Drawing.Size(86, 17);
this.ServerPingTypeLabel.TabIndex = 2;
@@ -246,7 +214,7 @@ namespace Netch.Forms
// ICMPingRadioBtn
//
this.ICMPingRadioBtn.AutoSize = true;
this.ICMPingRadioBtn.Location = new System.Drawing.Point(268, 63);
this.ICMPingRadioBtn.Location = new System.Drawing.Point(268, 34);
this.ICMPingRadioBtn.Name = "ICMPingRadioBtn";
this.ICMPingRadioBtn.Size = new System.Drawing.Size(75, 21);
this.ICMPingRadioBtn.TabIndex = 3;
@@ -257,7 +225,7 @@ namespace Netch.Forms
// TCPingRadioBtn
//
this.TCPingRadioBtn.AutoSize = true;
this.TCPingRadioBtn.Location = new System.Drawing.Point(366, 64);
this.TCPingRadioBtn.Location = new System.Drawing.Point(366, 35);
this.TCPingRadioBtn.Name = "TCPingRadioBtn";
this.TCPingRadioBtn.Size = new System.Drawing.Size(66, 21);
this.TCPingRadioBtn.TabIndex = 4;
@@ -356,12 +324,12 @@ namespace Netch.Forms
this.NFTabPage.BackColor = System.Drawing.SystemColors.ButtonFace;
this.NFTabPage.Controls.Add(this.ProcessFilterProtocolLabel);
this.NFTabPage.Controls.Add(this.ProcessFilterProtocolComboBox);
this.NFTabPage.Controls.Add(this.DNSHijackCheckBox);
this.NFTabPage.Controls.Add(this.DNSHijackHostTextBox);
this.NFTabPage.Controls.Add(this.FilterICMPCheckBox);
this.NFTabPage.Controls.Add(this.DNSHijackLabel);
this.NFTabPage.Controls.Add(this.ICMPDelayLabel);
this.NFTabPage.Controls.Add(this.ICMPDelayTextBox);
this.NFTabPage.Controls.Add(this.RedirectorSSCheckBox);
this.NFTabPage.Controls.Add(this.DNSHijackCheckBox);
this.NFTabPage.Controls.Add(this.DNSHijackHostTextBox);
this.NFTabPage.Controls.Add(this.ChildProcessHandleCheckBox);
this.NFTabPage.Location = new System.Drawing.Point(4, 29);
this.NFTabPage.Name = "NFTabPage";
@@ -388,71 +356,60 @@ namespace Netch.Forms
this.ProcessFilterProtocolComboBox.Size = new System.Drawing.Size(98, 25);
this.ProcessFilterProtocolComboBox.TabIndex = 1;
//
// DNSHijackCheckBox
//
this.DNSHijackCheckBox.AutoSize = true;
this.DNSHijackCheckBox.Location = new System.Drawing.Point(15, 50);
this.DNSHijackCheckBox.Name = "DNSHijackCheckBox";
this.DNSHijackCheckBox.Size = new System.Drawing.Size(196, 21);
this.DNSHijackCheckBox.TabIndex = 2;
this.DNSHijackCheckBox.Text = "Handle process\'s DNS Hijack";
this.DNSHijackCheckBox.UseVisualStyleBackColor = true;
//
// DNSHijackHostTextBox
//
this.DNSHijackHostTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.DNSHijackCheckBox, "Checked", true));
this.DNSHijackHostTextBox.Location = new System.Drawing.Point(237, 48);
this.DNSHijackHostTextBox.Name = "DNSHijackHostTextBox";
this.DNSHijackHostTextBox.Size = new System.Drawing.Size(191, 23);
this.DNSHijackHostTextBox.TabIndex = 3;
this.DNSHijackHostTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// FilterICMPCheckBox
//
this.FilterICMPCheckBox.AutoSize = true;
this.FilterICMPCheckBox.Location = new System.Drawing.Point(13, 80);
this.FilterICMPCheckBox.Location = new System.Drawing.Point(15, 50);
this.FilterICMPCheckBox.Name = "FilterICMPCheckBox";
this.FilterICMPCheckBox.Size = new System.Drawing.Size(90, 21);
this.FilterICMPCheckBox.TabIndex = 4;
this.FilterICMPCheckBox.TabIndex = 2;
this.FilterICMPCheckBox.Text = "Filter ICMP";
this.FilterICMPCheckBox.UseVisualStyleBackColor = true;
//
// ICMPDelayLabel
//
this.ICMPDelayLabel.AutoSize = true;
this.ICMPDelayLabel.Location = new System.Drawing.Point(30, 110);
this.ICMPDelayLabel.Location = new System.Drawing.Point(65, 80);
this.ICMPDelayLabel.Name = "ICMPDelayLabel";
this.ICMPDelayLabel.Size = new System.Drawing.Size(100, 17);
this.ICMPDelayLabel.TabIndex = 5;
this.ICMPDelayLabel.TabIndex = 3;
this.ICMPDelayLabel.Text = "ICMP Delay(ms)";
//
// ICMPDelayTextBox
//
this.ICMPDelayTextBox.Location = new System.Drawing.Point(237, 107);
this.ICMPDelayTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.FilterICMPCheckBox, "Checked", true));
this.ICMPDelayTextBox.Location = new System.Drawing.Point(237, 77);
this.ICMPDelayTextBox.Name = "ICMPDelayTextBox";
this.ICMPDelayTextBox.ReadOnly = true;
this.ICMPDelayTextBox.Size = new System.Drawing.Size(98, 23);
this.ICMPDelayTextBox.TabIndex = 6;
this.ICMPDelayTextBox.TabIndex = 4;
this.ICMPDelayTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// RedirectorSSCheckBox
// DNSHijackCheckBox
//
this.RedirectorSSCheckBox.AutoSize = true;
this.RedirectorSSCheckBox.Location = new System.Drawing.Point(15, 140);
this.RedirectorSSCheckBox.Name = "RedirectorSSCheckBox";
this.RedirectorSSCheckBox.Size = new System.Drawing.Size(265, 21);
this.RedirectorSSCheckBox.TabIndex = 7;
this.RedirectorSSCheckBox.Text = "Redirector built-in Shadowsocks support";
this.RedirectorSSCheckBox.UseVisualStyleBackColor = true;
this.DNSHijackCheckBox.AutoSize = true;
this.DNSHijackCheckBox.Location = new System.Drawing.Point(15, 110);
this.DNSHijackCheckBox.Name = "DNSHijackCheckBox";
this.DNSHijackCheckBox.Size = new System.Drawing.Size(92, 21);
this.DNSHijackCheckBox.TabIndex = 5;
this.DNSHijackCheckBox.Text = "DNS Hijack";
this.DNSHijackCheckBox.UseVisualStyleBackColor = true;
//
// DNSHijackHostTextBox
//
this.DNSHijackHostTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.DNSHijackCheckBox, "Checked", true));
this.DNSHijackHostTextBox.Location = new System.Drawing.Point(237, 138);
this.DNSHijackHostTextBox.Name = "DNSHijackHostTextBox";
this.DNSHijackHostTextBox.Size = new System.Drawing.Size(191, 23);
this.DNSHijackHostTextBox.TabIndex = 6;
this.DNSHijackHostTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// ChildProcessHandleCheckBox
//
this.ChildProcessHandleCheckBox.AutoSize = true;
this.ChildProcessHandleCheckBox.Enabled = false;
this.ChildProcessHandleCheckBox.Location = new System.Drawing.Point(15, 170);
this.ChildProcessHandleCheckBox.Name = "ChildProcessHandleCheckBox";
this.ChildProcessHandleCheckBox.Size = new System.Drawing.Size(150, 21);
this.ChildProcessHandleCheckBox.TabIndex = 8;
this.ChildProcessHandleCheckBox.TabIndex = 7;
this.ChildProcessHandleCheckBox.Text = "Child Process Handle";
this.ChildProcessHandleCheckBox.UseVisualStyleBackColor = true;
//
@@ -773,6 +730,7 @@ namespace Netch.Forms
this.OtherTabPage.Controls.Add(this.MinimizeWhenStartedCheckBox);
this.OtherTabPage.Controls.Add(this.RunAtStartupCheckBox);
this.OtherTabPage.Controls.Add(this.CheckUpdateWhenOpenedCheckBox);
this.OtherTabPage.Controls.Add(this.NoSupportDialogCheckBox);
this.OtherTabPage.Controls.Add(this.CheckBetaUpdateCheckBox);
this.OtherTabPage.Controls.Add(this.UpdateServersWhenOpenedCheckBox);
this.OtherTabPage.Location = new System.Drawing.Point(4, 29);
@@ -846,13 +804,23 @@ namespace Netch.Forms
this.CheckUpdateWhenOpenedCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.CheckUpdateWhenOpenedCheckBox.UseVisualStyleBackColor = true;
//
// NoSupportDialogCheckBox
//
this.NoSupportDialogCheckBox.AutoSize = true;
this.NoSupportDialogCheckBox.Location = new System.Drawing.Point(6, 72);
this.NoSupportDialogCheckBox.Name = "NoSupportDialogCheckBox";
this.NoSupportDialogCheckBox.Size = new System.Drawing.Size(174, 21);
this.NoSupportDialogCheckBox.TabIndex = 6;
this.NoSupportDialogCheckBox.Text = "Disable Support Warning";
this.NoSupportDialogCheckBox.UseVisualStyleBackColor = true;
//
// CheckBetaUpdateCheckBox
//
this.CheckBetaUpdateCheckBox.AutoSize = true;
this.CheckBetaUpdateCheckBox.Location = new System.Drawing.Point(200, 72);
this.CheckBetaUpdateCheckBox.Name = "CheckBetaUpdateCheckBox";
this.CheckBetaUpdateCheckBox.Size = new System.Drawing.Size(137, 21);
this.CheckBetaUpdateCheckBox.TabIndex = 6;
this.CheckBetaUpdateCheckBox.TabIndex = 7;
this.CheckBetaUpdateCheckBox.Text = "Check Beta update";
this.CheckBetaUpdateCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.CheckBetaUpdateCheckBox.UseVisualStyleBackColor = true;
@@ -863,7 +831,7 @@ namespace Netch.Forms
this.UpdateServersWhenOpenedCheckBox.Location = new System.Drawing.Point(200, 94);
this.UpdateServersWhenOpenedCheckBox.Name = "UpdateServersWhenOpenedCheckBox";
this.UpdateServersWhenOpenedCheckBox.Size = new System.Drawing.Size(200, 21);
this.UpdateServersWhenOpenedCheckBox.TabIndex = 7;
this.UpdateServersWhenOpenedCheckBox.TabIndex = 8;
this.UpdateServersWhenOpenedCheckBox.Text = "Update Servers when opened";
this.UpdateServersWhenOpenedCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.UpdateServersWhenOpenedCheckBox.UseVisualStyleBackColor = true;
@@ -959,6 +927,15 @@ namespace Netch.Forms
this.flowLayoutPanel1.Size = new System.Drawing.Size(480, 400);
this.flowLayoutPanel1.TabIndex = 0;
//
// DNSHijackLabel
//
this.DNSHijackLabel.AutoSize = true;
this.DNSHijackLabel.Location = new System.Drawing.Point(65, 140);
this.DNSHijackLabel.Name = "DNSHijackLabel";
this.DNSHijackLabel.Size = new System.Drawing.Size(34, 17);
this.DNSHijackLabel.TabIndex = 3;
this.DNSHijackLabel.Text = "DNS";
//
// SettingForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
@@ -1008,11 +985,8 @@ namespace Netch.Forms
private System.Windows.Forms.TabPage v2rayTabPage;
private System.Windows.Forms.GroupBox PortGroupBox;
private System.Windows.Forms.CheckBox AllowDevicesCheckBox;
private System.Windows.Forms.Label HTTPPortLabel;
private System.Windows.Forms.TextBox HTTPPortTextBox;
private System.Windows.Forms.Label Socks5PortLabel;
private System.Windows.Forms.TextBox Socks5PortTextBox;
private System.Windows.Forms.CheckBox ResolveServerHostnameCheckBox;
private System.Windows.Forms.GroupBox WinTUNGroupBox;
private System.Windows.Forms.CheckBox ProxyDNSCheckBox;
private System.Windows.Forms.CheckBox UseCustomDNSCheckBox;
@@ -1070,7 +1044,6 @@ namespace Netch.Forms
private System.Windows.Forms.TextBox OtherDNSTextBox;
private System.Windows.Forms.TextBox ChinaDNSTextBox;
private System.Windows.Forms.TextBox DNSHijackHostTextBox;
private System.Windows.Forms.CheckBox RedirectorSSCheckBox;
private System.Windows.Forms.Label ServerPingTypeLabel;
private System.Windows.Forms.RadioButton TCPingRadioBtn;
private System.Windows.Forms.RadioButton ICMPingRadioBtn;
@@ -1080,5 +1053,7 @@ namespace Netch.Forms
private System.Windows.Forms.CheckBox ChildProcessHandleCheckBox;
private System.Windows.Forms.TextBox ICMPDelayTextBox;
private System.Windows.Forms.Label ICMPDelayLabel;
private System.Windows.Forms.CheckBox NoSupportDialogCheckBox;
private System.Windows.Forms.Label DNSHijackLabel;
}
}

View File

@@ -26,29 +26,19 @@ namespace Netch.Forms
#region General
BindTextBox<ushort>(Socks5PortTextBox,
p => p.ToString() != HTTPPortTextBox.Text,
p => Global.Settings.Socks5LocalPort = p,
Global.Settings.Socks5LocalPort);
BindTextBox<ushort>(HTTPPortTextBox,
p => p.ToString() != Socks5PortTextBox.Text,
p => Global.Settings.HTTPLocalPort = p,
Global.Settings.HTTPLocalPort);
BindTextBox<ushort>(Socks5PortTextBox, p => true, p => Global.Settings.Socks5LocalPort = p, Global.Settings.Socks5LocalPort);
BindCheckBox(AllowDevicesCheckBox,
c => Global.Settings.LocalAddress = AllowDevicesCheckBox.Checked ? "0.0.0.0" : "127.0.0.1",
Global.Settings.LocalAddress switch { "127.0.0.1" => false, "0.0.0.0" => true, _ => false });
BindCheckBox(ResolveServerHostnameCheckBox, c => Global.Settings.ResolveServerHostname = c, Global.Settings.ResolveServerHostname);
BindRadioBox(ICMPingRadioBtn, _ => { }, !Global.Settings.ServerTCPing);
BindRadioBox(TCPingRadioBtn, c => Global.Settings.ServerTCPing = c, Global.Settings.ServerTCPing);
BindTextBox<int>(ProfileCountTextBox, i => i > -1, i => Global.Settings.ProfileCount = i, Global.Settings.ProfileCount);
BindTextBox<int>(DetectionTickTextBox,
i => ServerHelper.DelayTestHelper.Range.InRange(i),
i => DelayTestHelper.Range.InRange(i),
i => Global.Settings.DetectionTick = i,
Global.Settings.DetectionTick);
@@ -104,16 +94,14 @@ namespace Netch.Forms
Enum.GetNames(typeof(PortType)),
Global.Settings.Redirector.FilterProtocol.ToString());
BindCheckBox(FilterICMPCheckBox, b => Global.Settings.Redirector.FilterICMP = b, Global.Settings.Redirector.FilterICMP);
BindTextBox<int>(ICMPDelayTextBox, s => true, s => Global.Settings.Redirector.ICMPDelay = s, Global.Settings.Redirector.ICMPDelay);
BindCheckBox(DNSHijackCheckBox, b => Global.Settings.Redirector.DNSHijack = b, Global.Settings.Redirector.DNSHijack);
BindTextBox(DNSHijackHostTextBox, s => true, s => Global.Settings.Redirector.DNSHijackHost = s, Global.Settings.Redirector.DNSHijackHost);
BindCheckBox(FilterICMPCheckBox, b => Global.Settings.Redirector.FilterICMP = b, Global.Settings.Redirector.FilterICMP);
BindTextBox(ICMPDelayTextBox, s => int.TryParse(s, out _), s => { }, Global.Settings.Redirector.ICMPDelay);
BindCheckBox(RedirectorSSCheckBox, s => Global.Settings.Redirector.RedirectorSS = s, Global.Settings.Redirector.RedirectorSS);
BindCheckBox(ChildProcessHandleCheckBox,
s => Global.Settings.Redirector.ChildProcessHandle = s,
Global.Settings.Redirector.ChildProcessHandle);
@@ -205,6 +193,8 @@ namespace Netch.Forms
BindCheckBox(UpdateServersWhenOpenedCheckBox, b => Global.Settings.UpdateServersWhenOpened = b, Global.Settings.UpdateServersWhenOpened);
BindCheckBox(NoSupportDialogCheckBox, b => Global.Settings.NoSupportDialog = b, Global.Settings.NoSupportDialog);
#endregion
#region AioDNS

View File

@@ -1,6 +1,6 @@
namespace Netch.Forms
{
partial class SubscribeForm
partial class SubscriptionForm
{
/// <summary>
/// Required designer variable.
@@ -29,24 +29,23 @@
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SubscribeForm));
this.AddSubscriptionBox = new System.Windows.Forms.GroupBox();
this.RemarkLabel = new System.Windows.Forms.Label();
this.RemarkTextBox = new System.Windows.Forms.TextBox();
this.LinkLabel = new System.Windows.Forms.Label();
this.LinkTextBox = new System.Windows.Forms.TextBox();
this.UserAgentLabel = new System.Windows.Forms.Label();
this.UserAgentTextBox = new System.Windows.Forms.TextBox();
this.UnselectButton = new System.Windows.Forms.Button();
this.AddButton = new System.Windows.Forms.Button();
this.UserAgentLabel = new System.Windows.Forms.Label();
this.LinkTextBox = new System.Windows.Forms.TextBox();
this.LinkLabel = new System.Windows.Forms.Label();
this.RemarkTextBox = new System.Windows.Forms.TextBox();
this.RemarkLabel = new System.Windows.Forms.Label();
this.SubscribeLinkListView = new System.Windows.Forms.ListView();
this.SubscriptionLinkListView = new System.Windows.Forms.ListView();
this.EnableColumnHeader = new System.Windows.Forms.ColumnHeader();
this.RemarkColumnHeader = new System.Windows.Forms.ColumnHeader();
this.LinkColumnHeader = new System.Windows.Forms.ColumnHeader();
this.UserAgentHeader = new System.Windows.Forms.ColumnHeader();
this.pContextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.deleteServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.DeleteServersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CopyLinkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MainTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
this.AddSubscriptionBox.SuspendLayout();
@@ -56,14 +55,14 @@
//
// AddSubscriptionBox
//
this.AddSubscriptionBox.Controls.Add(this.RemarkLabel);
this.AddSubscriptionBox.Controls.Add(this.RemarkTextBox);
this.AddSubscriptionBox.Controls.Add(this.LinkLabel);
this.AddSubscriptionBox.Controls.Add(this.LinkTextBox);
this.AddSubscriptionBox.Controls.Add(this.UserAgentLabel);
this.AddSubscriptionBox.Controls.Add(this.UserAgentTextBox);
this.AddSubscriptionBox.Controls.Add(this.UnselectButton);
this.AddSubscriptionBox.Controls.Add(this.AddButton);
this.AddSubscriptionBox.Controls.Add(this.UserAgentLabel);
this.AddSubscriptionBox.Controls.Add(this.LinkTextBox);
this.AddSubscriptionBox.Controls.Add(this.LinkLabel);
this.AddSubscriptionBox.Controls.Add(this.RemarkTextBox);
this.AddSubscriptionBox.Controls.Add(this.RemarkLabel);
this.AddSubscriptionBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.AddSubscriptionBox.Location = new System.Drawing.Point(8, 248);
this.AddSubscriptionBox.Name = "AddSubscriptionBox";
@@ -71,6 +70,47 @@
this.AddSubscriptionBox.TabIndex = 1;
this.AddSubscriptionBox.TabStop = false;
//
// RemarkLabel
//
this.RemarkLabel.AutoSize = true;
this.RemarkLabel.Location = new System.Drawing.Point(11, 19);
this.RemarkLabel.Name = "RemarkLabel";
this.RemarkLabel.Size = new System.Drawing.Size(53, 17);
this.RemarkLabel.TabIndex = 1;
this.RemarkLabel.Text = "Remark";
//
// RemarkTextBox
//
this.RemarkTextBox.Location = new System.Drawing.Point(109, 16);
this.RemarkTextBox.Name = "RemarkTextBox";
this.RemarkTextBox.Size = new System.Drawing.Size(545, 23);
this.RemarkTextBox.TabIndex = 2;
//
// LinkLabel
//
this.LinkLabel.AutoSize = true;
this.LinkLabel.Location = new System.Drawing.Point(11, 48);
this.LinkLabel.Name = "LinkLabel";
this.LinkLabel.Size = new System.Drawing.Size(31, 17);
this.LinkLabel.TabIndex = 3;
this.LinkLabel.Text = "Link";
//
// LinkTextBox
//
this.LinkTextBox.Location = new System.Drawing.Point(109, 45);
this.LinkTextBox.Name = "LinkTextBox";
this.LinkTextBox.Size = new System.Drawing.Size(545, 23);
this.LinkTextBox.TabIndex = 4;
//
// UserAgentLabel
//
this.UserAgentLabel.AutoSize = true;
this.UserAgentLabel.Location = new System.Drawing.Point(11, 77);
this.UserAgentLabel.Name = "UserAgentLabel";
this.UserAgentLabel.Size = new System.Drawing.Size(74, 17);
this.UserAgentLabel.TabIndex = 5;
this.UserAgentLabel.Text = "User-Agent";
//
// UserAgentTextBox
//
this.UserAgentTextBox.Location = new System.Drawing.Point(109, 74);
@@ -98,68 +138,28 @@
this.AddButton.UseVisualStyleBackColor = true;
this.AddButton.Click += new System.EventHandler(this.AddButton_Click);
//
// UserAgentLabel
// SubscriptionLinkListView
//
this.UserAgentLabel.AutoSize = true;
this.UserAgentLabel.Location = new System.Drawing.Point(11, 77);
this.UserAgentLabel.Name = "UserAgentLabel";
this.UserAgentLabel.Size = new System.Drawing.Size(74, 17);
this.UserAgentLabel.TabIndex = 5;
this.UserAgentLabel.Text = "User-Agent";
//
// LinkTextBox
//
this.LinkTextBox.Location = new System.Drawing.Point(109, 45);
this.LinkTextBox.Name = "LinkTextBox";
this.LinkTextBox.Size = new System.Drawing.Size(545, 23);
this.LinkTextBox.TabIndex = 4;
//
// LinkLabel
//
this.LinkLabel.AutoSize = true;
this.LinkLabel.Location = new System.Drawing.Point(11, 48);
this.LinkLabel.Name = "LinkLabel";
this.LinkLabel.Size = new System.Drawing.Size(31, 17);
this.LinkLabel.TabIndex = 3;
this.LinkLabel.Text = "Link";
//
// RemarkTextBox
//
this.RemarkTextBox.Location = new System.Drawing.Point(109, 16);
this.RemarkTextBox.Name = "RemarkTextBox";
this.RemarkTextBox.Size = new System.Drawing.Size(545, 23);
this.RemarkTextBox.TabIndex = 2;
//
// RemarkLabel
//
this.RemarkLabel.AutoSize = true;
this.RemarkLabel.Location = new System.Drawing.Point(11, 19);
this.RemarkLabel.Name = "RemarkLabel";
this.RemarkLabel.Size = new System.Drawing.Size(53, 17);
this.RemarkLabel.TabIndex = 1;
this.RemarkLabel.Text = "Remark";
//
// SubscribeLinkListView
//
this.SubscribeLinkListView.AllowColumnReorder = true;
this.SubscribeLinkListView.CheckBoxes = true;
this.SubscribeLinkListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[]
{
this.EnableColumnHeader, this.RemarkColumnHeader, this.LinkColumnHeader, this.UserAgentHeader
});
this.SubscribeLinkListView.Dock = System.Windows.Forms.DockStyle.Fill;
this.SubscribeLinkListView.FullRowSelect = true;
this.SubscribeLinkListView.HideSelection = false;
this.SubscribeLinkListView.Location = new System.Drawing.Point(8, 8);
this.SubscribeLinkListView.MultiSelect = false;
this.SubscribeLinkListView.Name = "SubscribeLinkListView";
this.SubscribeLinkListView.Size = new System.Drawing.Size(668, 234);
this.SubscribeLinkListView.TabIndex = 0;
this.SubscribeLinkListView.UseCompatibleStateImageBehavior = false;
this.SubscribeLinkListView.View = System.Windows.Forms.View.Details;
this.SubscribeLinkListView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.SubscribeLinkListView_ItemChecked);
this.SubscribeLinkListView.SelectedIndexChanged += new System.EventHandler(this.SubscribeLinkListView_SelectedIndexChanged);
this.SubscribeLinkListView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.SubscribeLinkListView_MouseUp);
this.SubscriptionLinkListView.AllowColumnReorder = true;
this.SubscriptionLinkListView.CheckBoxes = true;
this.SubscriptionLinkListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.EnableColumnHeader,
this.RemarkColumnHeader,
this.LinkColumnHeader,
this.UserAgentHeader});
this.SubscriptionLinkListView.Dock = System.Windows.Forms.DockStyle.Fill;
this.SubscriptionLinkListView.FullRowSelect = true;
this.SubscriptionLinkListView.HideSelection = false;
this.SubscriptionLinkListView.Location = new System.Drawing.Point(8, 8);
this.SubscriptionLinkListView.MultiSelect = false;
this.SubscriptionLinkListView.Name = "SubscriptionLinkListView";
this.SubscriptionLinkListView.Size = new System.Drawing.Size(668, 234);
this.SubscriptionLinkListView.TabIndex = 0;
this.SubscriptionLinkListView.UseCompatibleStateImageBehavior = false;
this.SubscriptionLinkListView.View = System.Windows.Forms.View.Details;
this.SubscriptionLinkListView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.SubscriptionLinkListView_ItemChecked);
this.SubscriptionLinkListView.SelectedIndexChanged += new System.EventHandler(this.SubscriptionLinkListView_SelectedIndexChanged);
this.SubscriptionLinkListView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.SubscriptionLinkListView_MouseUp);
//
// EnableColumnHeader
//
@@ -182,39 +182,39 @@
//
// pContextMenuStrip
//
this.pContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[]
{
this.DeleteToolStripMenuItem, this.deleteServerToolStripMenuItem, this.CopyLinkToolStripMenuItem
});
this.pContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.DeleteToolStripMenuItem,
this.DeleteServersToolStripMenuItem,
this.CopyLinkToolStripMenuItem});
this.pContextMenuStrip.Name = "pContextMenuStrip";
this.pContextMenuStrip.Size = new System.Drawing.Size(151, 70);
this.pContextMenuStrip.Size = new System.Drawing.Size(161, 70);
//
// DeleteToolStripMenuItem
//
this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem";
this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(150, 22);
this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(160, 22);
this.DeleteToolStripMenuItem.Text = "Delete";
this.DeleteToolStripMenuItem.Click += new System.EventHandler(this.DeleteToolStripMenuItem_Click);
//
// deleteServerToolStripMenuItem
// DeleteServersToolStripMenuItem
//
this.deleteServerToolStripMenuItem.Name = "deleteServerToolStripMenuItem";
this.deleteServerToolStripMenuItem.Size = new System.Drawing.Size(150, 22);
this.deleteServerToolStripMenuItem.Text = "DeleteServer";
this.deleteServerToolStripMenuItem.Click += new System.EventHandler(this.deleteServerToolStripMenuItem_Click);
this.DeleteServersToolStripMenuItem.Name = "DeleteServersToolStripMenuItem";
this.DeleteServersToolStripMenuItem.Size = new System.Drawing.Size(160, 22);
this.DeleteServersToolStripMenuItem.Text = "Delete Servers";
this.DeleteServersToolStripMenuItem.Click += new System.EventHandler(this.DeleteServersToolStripMenuItem_Click);
//
// CopyLinkToolStripMenuItem
//
this.CopyLinkToolStripMenuItem.Name = "CopyLinkToolStripMenuItem";
this.CopyLinkToolStripMenuItem.Size = new System.Drawing.Size(150, 22);
this.CopyLinkToolStripMenuItem.Text = "CopyLink";
this.CopyLinkToolStripMenuItem.Size = new System.Drawing.Size(160, 22);
this.CopyLinkToolStripMenuItem.Text = "Copy link";
this.CopyLinkToolStripMenuItem.Click += new System.EventHandler(this.CopyLinkToolStripMenuItem_Click);
//
// MainTableLayoutPanel
//
this.MainTableLayoutPanel.ColumnCount = 1;
this.MainTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.MainTableLayoutPanel.Controls.Add(this.SubscribeLinkListView, 0, 0);
this.MainTableLayoutPanel.Controls.Add(this.SubscriptionLinkListView, 0, 0);
this.MainTableLayoutPanel.Controls.Add(this.AddSubscriptionBox, 0, 1);
this.MainTableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.MainTableLayoutPanel.Location = new System.Drawing.Point(0, 0);
@@ -227,25 +227,26 @@
this.MainTableLayoutPanel.Size = new System.Drawing.Size(684, 391);
this.MainTableLayoutPanel.TabIndex = 11;
//
// SubscribeForm
// SubscriptionForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(684, 391);
this.Controls.Add(this.MainTableLayoutPanel);
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (134)));
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.Name = "SubscribeForm";
this.Name = "SubscriptionForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Subscribe";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.SubscribeForm_FormClosing);
this.Text = "Subscription";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.SubscriptionForm_FormClosing);
this.AddSubscriptionBox.ResumeLayout(false);
this.AddSubscriptionBox.PerformLayout();
this.pContextMenuStrip.ResumeLayout(false);
this.MainTableLayoutPanel.ResumeLayout(false);
this.ResumeLayout(false);
}
private System.Windows.Forms.ColumnHeader EnableColumnHeader;
@@ -257,7 +258,7 @@
private System.Windows.Forms.Label LinkLabel;
private System.Windows.Forms.TextBox RemarkTextBox;
private System.Windows.Forms.Button AddButton;
private System.Windows.Forms.ListView SubscribeLinkListView;
private System.Windows.Forms.ListView SubscriptionLinkListView;
private System.Windows.Forms.ColumnHeader RemarkColumnHeader;
private System.Windows.Forms.ColumnHeader LinkColumnHeader;
private System.Windows.Forms.ContextMenuStrip pContextMenuStrip;
@@ -269,6 +270,6 @@
#endregion
private System.Windows.Forms.ToolStripMenuItem deleteServerToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem DeleteServersToolStripMenuItem;
}
}

View File

@@ -1,15 +1,15 @@
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
using System;
using System;
using System.Linq;
using System.Windows.Forms;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
namespace Netch.Forms
{
public partial class SubscribeForm : Form
public partial class SubscriptionForm : Form
{
public SubscribeForm()
public SubscriptionForm()
{
InitializeComponent();
Icon = Resources.icon;
@@ -17,33 +17,33 @@ namespace Netch.Forms
i18N.TranslateForm(this);
i18N.TranslateForm(pContextMenuStrip);
InitSubscribeLink();
LoadSubscriptionLinks();
}
private int SelectedIndex
{
get
{
if (SubscribeLinkListView.MultiSelect)
if (SubscriptionLinkListView.MultiSelect)
throw new Exception();
return SubscribeLinkListView.SelectedIndices.Count == 0 ? -1 : SubscribeLinkListView.SelectedIndices[0];
return SubscriptionLinkListView.SelectedIndices.Count == 0 ? -1 : SubscriptionLinkListView.SelectedIndices[0];
}
}
#region EventHandler
private void SubscribeLinkListView_MouseUp(object sender, MouseEventArgs e)
private void SubscriptionLinkListView_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
if (SelectedIndex != -1)
pContextMenuStrip.Show(SubscribeLinkListView, e.Location);
pContextMenuStrip.Show(SubscriptionLinkListView, e.Location);
}
/// <summary>
/// 选中/取消选中
/// </summary>
private void SubscribeLinkListView_SelectedIndexChanged(object sender, EventArgs e)
private void SubscriptionLinkListView_SelectedIndexChanged(object sender, EventArgs e)
{
SetEditingGroup(SelectedIndex);
}
@@ -51,13 +51,13 @@ namespace Netch.Forms
/// <summary>
/// 订阅启/禁用
/// </summary>
private void SubscribeLinkListView_ItemChecked(object sender, ItemCheckedEventArgs e)
private void SubscriptionLinkListView_ItemChecked(object sender, ItemCheckedEventArgs e)
{
var index = e.Item.Index;
Global.Settings.SubscribeLink[index].Enable = SubscribeLinkListView.Items[index].Checked;
Global.Settings.Subscription[index].Enable = SubscriptionLinkListView.Items[index].Checked;
}
private async void SubscribeForm_FormClosing(object sender, FormClosingEventArgs e)
private async void SubscriptionForm_FormClosing(object sender, FormClosingEventArgs e)
{
await Configuration.SaveAsync();
}
@@ -94,13 +94,13 @@ namespace Netch.Forms
if (SelectedIndex == -1)
{
if (Global.Settings.SubscribeLink.Any(link => link.Remark.Equals(RemarkTextBox.Text)))
if (Global.Settings.Subscription.Any(link => link.Remark.Equals(RemarkTextBox.Text)))
{
MessageBoxX.Show("Remark Name Duplicate!");
MessageBoxX.Show(i18N.Translate("Subscription with the specified remark already exists"));
return;
}
Global.Settings.SubscribeLink.Add(new SubscribeLink
Global.Settings.Subscription.Add(new Subscription
{
Enable = true,
Remark = RemarkTextBox.Text,
@@ -110,7 +110,7 @@ namespace Netch.Forms
}
else
{
var subscribeLink = Global.Settings.SubscribeLink[SelectedIndex];
var subscribeLink = Global.Settings.Subscription[SelectedIndex];
RenameServers(subscribeLink.Remark, RemarkTextBox.Text);
subscribeLink.Link = LinkTextBox.Text;
@@ -118,7 +118,7 @@ namespace Netch.Forms
subscribeLink.UserAgent = UserAgentTextBox.Text;
}
InitSubscribeLink();
LoadSubscriptionLinks();
}
#endregion
@@ -131,24 +131,24 @@ namespace Netch.Forms
confirm: true) != DialogResult.OK)
return;
var subscribeLink = Global.Settings.SubscribeLink[SelectedIndex];
var subscribeLink = Global.Settings.Subscription[SelectedIndex];
DeleteServers(subscribeLink.Remark);
Global.Settings.SubscribeLink.Remove(subscribeLink);
Global.Settings.Subscription.Remove(subscribeLink);
InitSubscribeLink();
LoadSubscriptionLinks();
}
private void deleteServerToolStripMenuItem_Click(object sender, EventArgs e)
private void DeleteServersToolStripMenuItem_Click(object sender, EventArgs e)
{
if (MessageBoxX.Show(i18N.Translate("Confirm deletion?"), confirm: true) != DialogResult.OK)
return;
DeleteServers(Global.Settings.SubscribeLink[SelectedIndex].Remark);
DeleteServers(Global.Settings.Subscription[SelectedIndex].Remark);
}
private void CopyLinkToolStripMenuItem_Click(object sender, EventArgs e)
{
Clipboard.SetText(Global.Settings.SubscribeLink[SelectedIndex].Link);
Clipboard.SetText(Global.Settings.Subscription[SelectedIndex].Link);
}
#endregion
@@ -166,12 +166,12 @@ namespace Netch.Forms
server.Group = newGroup;
}
private void InitSubscribeLink()
private void LoadSubscriptionLinks()
{
SubscribeLinkListView.Items.Clear();
SubscriptionLinkListView.Items.Clear();
foreach (var item in Global.Settings.SubscribeLink)
SubscribeLinkListView.Items.Add(new ListViewItem(new[]
foreach (var item in Global.Settings.Subscription)
SubscriptionLinkListView.Items.Add(new ListViewItem(new[]
{
"",
item.Remark,
@@ -202,7 +202,7 @@ namespace Netch.Forms
return;
}
var item = Global.Settings.SubscribeLink[index];
var item = Global.Settings.Subscription[index];
AddSubscriptionBox.Text = item.Remark;
RemarkTextBox.Text = item.Remark;
LinkTextBox.Text = item.Link;

View File

@@ -0,0 +1,63 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="pContextMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Windows.Forms;
using Netch.Forms;
using Netch.Models;
@@ -42,7 +43,7 @@ namespace Netch
public static JsonSerializerOptions NewCustomJsonSerializerOptions() => new()
{
WriteIndented = true,
IgnoreNullValues = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
}

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,11 @@
using System.Threading.Tasks;
using Netch.Models;
using Netch.Servers;
namespace Netch.Interfaces
{
public interface IModeController : IController
{
public void Start(Server server, Mode mode);
public Task StartAsync(Socks5Server 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<Socks5LocalServer> 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
@@ -7,58 +8,45 @@ namespace Netch.Interops
{
public enum NameList
{
TYPE_FILTERLOOPBACK,
TYPE_FILTERICMP,
TYPE_FILTERTCP,
TYPE_FILTERUDP,
AIO_FILTERLOOPBACK,
AIO_FILTERINTRANET, // LAN
AIO_FILTERPARENT,
AIO_FILTERICMP,
AIO_FILTERTCP,
AIO_FILTERUDP,
AIO_FILTERDNS,
TYPE_CLRNAME,
TYPE_ADDNAME,
TYPE_BYPNAME,
AIO_ICMPING,
TYPE_DNSHOST,
AIO_DNSHOST,
AIO_DNSPORT,
TYPE_TCPLISN,
TYPE_TCPTYPE,
TYPE_TCPHOST,
TYPE_TCPUSER,
TYPE_TCPPASS,
TYPE_TCPMETH,
TYPE_TCPPROT,
TYPE_TCPPRPA,
TYPE_TCPOBFS,
TYPE_TCPOBPA,
AIO_TGTHOST,
AIO_TGTPORT,
AIO_TGTUSER,
AIO_TGTPASS,
TYPE_UDPLISN,
TYPE_UDPTYPE,
TYPE_UDPHOST,
TYPE_UDPUSER,
TYPE_UDPPASS,
TYPE_UDPMETH,
TYPE_UDPPROT,
TYPE_UDPPRPA,
TYPE_UDPOBFS,
TYPE_UDPOBPA
AIO_CLRNAME,
AIO_ADDNAME,
AIO_BYPNAME
}
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;
private const string Redirector_bin = "Redirector.bin";
[DllImport(Redirector_bin, CallingConvention = CallingConvention.Cdecl)]
@@ -70,10 +58,12 @@ namespace Netch.Interops
[DllImport(Redirector_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern bool aio_free();
/*
[DllImport(Redirector_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern ulong aio_getUP();
[DllImport(Redirector_bin, CallingConvention = CallingConvention.Cdecl)]
private static extern ulong aio_getDL();
*/
}
}

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,9 +1,10 @@
using Netch.Models;
using System;
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Netch.Models;
using Netch.Utils;
namespace Netch.Utils
namespace Netch.JsonConverter
{
public class ServerConverterWithTypeDiscriminator : JsonConverter<Server>
{
@@ -11,18 +12,9 @@ namespace Netch.Utils
public override Server Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var jsonElement = JsonSerializer.Deserialize<JsonElement>(ref reader)!;
try
{
var type = ServerHelper.GetTypeByTypeName(jsonElement.GetProperty("Type").GetString()!);
return (Server)JsonSerializer.Deserialize(jsonElement.GetRawText(), type)!;
}
catch
{
// Unsupported Server Type
return JsonSerializer.Deserialize<Server>(jsonElement.GetRawText(), new JsonSerializerOptions())!;
}
var jsonElement = JsonSerializer.Deserialize<JsonElement>(ref reader);
var type = ServerHelper.GetTypeByTypeName(jsonElement.GetProperty("Type").GetString()!);
return (Server)jsonElement.Deserialize(type)!;
}
public override void Write(Utf8JsonWriter writer, Server value, JsonSerializerOptions options)

49
Netch/Models/Arguments.cs Normal file
View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Netch.Utils;
namespace Netch.Models
{
public static class Arguments
{
public static string Format(IEnumerable<object?> a)
{
var arguments = a.ToList();
if (arguments.Count % 2 != 0)
throw new FormatException("missing last argument value");
var tokens = new List<string>();
for (var i = 0; i < arguments.Count; i += 2)
{
var keyObj = arguments[i];
var valueObj = arguments[i + 1];
if (keyObj is not string key)
throw new FormatException($"argument key at array index {i} is not string");
switch (valueObj)
{
case SpecialArgument.Flag:
tokens.Add(key);
break;
case null:
case string value when value.IsNullOrWhiteSpace():
continue;
default:
tokens.Add(key);
tokens.Add(valueObj.ToString()!);
break;
}
}
return string.Join(' ', tokens);
}
}
public enum SpecialArgument
{
Flag
}
}

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Generic;
namespace Netch.Models.GitHubRelease
{

View File

@@ -120,14 +120,4 @@ namespace Netch.Models
return $"[{(int)Type + 1}] {i18N.Translate(Remark)}";
}
}
public static class ModeExtension
{
/// 是否会转发 UDP
public static bool TestNatRequired(this Mode mode)
{
return mode.Type is ModeType.Process && Global.Settings.Redirector.FilterProtocol.HasFlag(PortType.UDP) ||
mode.Type is ModeType.BypassRuleIPs;
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Netch.Models
{
public struct NatTypeTestResult
{
public string? Result;
public string? LocalEnd;
public string? PublicEnd;
}
}

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

@@ -1,120 +0,0 @@
using System;
using System.Linq;
using System.Reflection;
namespace Netch.Models
{
public abstract class ParameterBase
{
// null value par
private readonly bool _full;
protected readonly string ParametersSeparate = " ";
protected readonly string Separate = " ";
protected readonly string VerbPrefix = "-";
protected readonly string FullPrefix = "--";
protected ParameterBase()
{
_full = !GetType().IsDefined(typeof(VerbAttribute));
}
public override string ToString()
{
var parameters = GetType().GetProperties().Select(PropToParameter).Where(s => s != null).Cast<string>();
return string.Join(ParametersSeparate, parameters).Trim();
}
private string? PropToParameter(PropertyInfo p)
{
// prefix
bool full;
if (p.IsDefined(typeof(VerbAttribute)))
full = false;
else if (p.IsDefined(typeof(FullAttribute)))
full = true;
else
full = _full;
var prefix = full ? FullPrefix : VerbPrefix;
// key
var key = p.GetCustomAttribute<RealNameAttribute>()?.Name ?? p.Name;
// build
var value = p.GetValue(this);
switch (value)
{
case bool b:
return b ? $"{prefix}{key}" : null;
default:
if (string.IsNullOrWhiteSpace(value?.ToString()))
return p.IsDefined(typeof(OptionalAttribute)) ? null : throw new RequiredArgumentValueInvalidException(p.Name, this, null);
if (p.IsDefined(typeof(QuoteAttribute)))
value = $"\"{value}\"";
return $"{prefix}{key}{Separate}{value}";
}
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
public class VerbAttribute : Attribute
{
// Don't use verb and full both on one class or property
// if you did, will take verb
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
public class FullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class OptionalAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class QuoteAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class RealNameAttribute : Attribute
{
public string Name { get; }
public RealNameAttribute(string name)
{
Name = name;
}
}
[Serializable]
public class RequiredArgumentValueInvalidException : Exception
{
public string? ArgumentName { get; }
public object? ArgumentObject { get; }
private readonly string? _message;
private const string DefaultMessage = "{0}'s Argument \"{1}\" value invalid. A required argument's value can't be null or empty.";
public override string Message => _message ?? string.Format(DefaultMessage, ArgumentObject!.GetType(), ArgumentName);
public RequiredArgumentValueInvalidException()
{
_message = "Some Argument value invalid. A required argument value's can't be null or empty.";
}
public RequiredArgumentValueInvalidException(string argumentName, object argumentObject, string? message)
{
ArgumentName = argumentName;
ArgumentObject = argumentObject;
_message = message;
}
}
}

View File

@@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Netch.Utils;
@@ -17,7 +17,7 @@ namespace Netch.Models
/// <summary>
/// 组
/// </summary>
public string Group { get; set; } = "None";
public string Group { get; set; } = Constants.DefaultGroup;
/// <summary>
/// 地址
@@ -42,12 +42,7 @@ namespace Netch.Models
/// <summary>
/// 代理类型
/// </summary>
public virtual string Type { get; } = string.Empty;
[JsonExtensionData]
// ReSharper disable once CollectionNeverUpdated.Global
public Dictionary<string, object> ExtensionData { get; set; } = new();
public abstract string Type { get; }
public object Clone()
{
@@ -62,56 +57,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 = 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 +109,15 @@ namespace Netch.Models
public static class ServerExtension
{
public static string AutoResolveHostname(this Server server)
public static async Task<string> AutoResolveHostnameAsync(this Server server, AddressFamily inet = AddressFamily.Unspecified)
{
return Global.Settings.ResolveServerHostname ? DnsUtils.Lookup(server.Hostname)!.ToString() : server.Hostname;
// ! MainController cached
return (await DnsUtils.LookupAsync(server.Hostname, inet))!.ToString();
}
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;
}
}
}

View File

@@ -1,6 +1,8 @@
using Netch.Utils;
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Linq;
using System.Text.Json;
using Netch.Utils;
namespace Netch.Models
{
@@ -37,7 +39,7 @@ namespace Netch.Models
/// <summary>
/// 使用自定义 DNS 设置
/// </summary>
public bool UseCustomDNS { get; set; } = true;
public bool UseCustomDNS { get; set; } = false;
/// <summary>
/// 全局绕过 IP 列表
@@ -72,7 +74,7 @@ namespace Netch.Models
public bool V2rayNShareLink { get; set; } = true;
public bool XrayCone { get; set; } = false;
public bool XrayCone { get; set; } = true;
}
public class AioDNSConfig
@@ -101,16 +103,10 @@ namespace Netch.Models
/// </summary>
public string DNSHijackHost { get; set; } = "1.1.1.1:53";
[JsonIgnore]
public int ICMPDelay { get; } = 0;
public int ICMPDelay { get; set; } = 0;
public bool FilterICMP { get; set; } = false;
/// <summary>
/// 是否使用RDR内置SS
/// </summary>
public bool RedirectorSS { get; set; } = false;
/// <summary>
/// 是否代理子进程
/// </summary>
@@ -196,11 +192,6 @@ namespace Netch.Models
/// </summary>
public int RequestTimeout { get; set; } = 10000;
/// <summary>
/// 解析服务器主机名
/// </summary>
public bool ResolveServerHostname { get; set; } = true;
/// <summary>
/// 是否开机启动软件
/// </summary>
@@ -249,7 +240,7 @@ namespace Netch.Models
/// <summary>
/// 订阅链接列表
/// </summary>
public List<SubscribeLink> SubscribeLink { get; set; } = new();
public List<Subscription> Subscription { get; set; } = new();
/// <summary>
/// TUNTAP 适配器配置
@@ -263,7 +254,23 @@ namespace Netch.Models
public V2rayConfig V2RayConfig { get; set; } = new();
public Setting Clone()
public bool NoSupportDialog { get; set; } = false;
#region Migration
[Obsolete]
public JsonElement SubscribeLink
{
set
{
if (Subscription == null! || !Subscription.Any())
Subscription = value.Deserialize<List<Subscription>>()!;
}
}
#endregion
public Setting ShallowCopy()
{
return (Setting)MemberwiseClone();
}

View File

@@ -1,6 +1,6 @@
namespace Netch.Models
{
public class SubscribeLink
public class Subscription
{
/// <summary>
/// 启用状态

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,35 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Versioning;
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.Enums;
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>
/// 应用程序的主入口点
@@ -35,16 +45,25 @@ namespace Netch
var binPath = Path.Combine(Global.NetchDir, "bin");
Environment.SetEnvironmentVariable("PATH", $"{Environment.GetEnvironmentVariable("PATH")};{binPath}");
if (!Directory.Exists("bin") || !Directory.EnumerateFileSystemEntries("bin").Any())
{
i18N.Load("System");
MessageBoxX.Show(i18N.Translate("Please extract all files then run the program!"));
Environment.Exit(2);
}
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)
{
@@ -74,13 +93,9 @@ namespace Netch
// 加载语言
i18N.Load(Global.Settings.Language);
if (!Directory.Exists("bin") || !Directory.EnumerateFileSystemEntries("bin").Any())
{
MessageBoxX.Show(i18N.Translate("Please extract all files then run the program!"));
Environment.Exit(2);
}
Task.Run(LogEnvironment);
Task.Run(LogEnvironment).Forget();
CheckClr();
CheckOS();
// 绑定错误捕获
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
@@ -92,22 +107,65 @@ 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("OS: {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)));
{
// TODO log level setting
Task.Run(() => Log.Debug("Third-party Drivers:\n{Drivers}", string.Join(Constants.EOF, SystemInfo.SystemDrivers(false)))).Forget();
Task.Run(() => Log.Debug("Running Processes: \n{Processes}", string.Join(Constants.EOF, SystemInfo.Processes(false)))).Forget();
}
}
private static void CheckClr()
{
var framework = Assembly.GetExecutingAssembly().GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
if (framework == null)
{
Log.Warning("TargetFrameworkAttribute null");
return;
}
var frameworkName = new FrameworkName(framework);
if (frameworkName.Version.Major != Environment.Version.Major)
{
Log.Information("CLR: {Version}", Environment.Version);
Flags.NoSupport = true;
if (!Global.Settings.NoSupportDialog)
MessageBoxX.Show(
i18N.TranslateFormat("{0} won't get developers' support, Please do not report any issues or seek help from developers.",
"CLR " + Environment.Version),
LogLevel.WARNING);
}
}
private static void CheckOS()
{
if (Environment.OSVersion.Version.Build < 17763)
{
Flags.NoSupport = true;
if (!Global.Settings.NoSupportDialog)
MessageBoxX.Show(
i18N.TranslateFormat("{0} won't get developers' support, Please do not report any issues or seek help from developers.",
Environment.OSVersion),
LogLevel.WARNING);
}
}
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
}
@@ -116,13 +174,13 @@ namespace Netch
Log.Logger = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Verbose()
.WriteTo.Async(c => c.Debug(outputTemplate: Constants.OutputTemplate))
#else
.MinimumLevel.Debug()
#endif
.WriteTo.Async(c => c.File(Path.Combine(Global.NetchDir, Constants.LogFile),
outputTemplate: Constants.OutputTemplate,
rollOnFileSizeLimit: false))
.WriteTo.Console(outputTemplate: Constants.OutputTemplate)
.MinimumLevel.Override(@"Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.CreateLogger();
@@ -130,7 +188,7 @@ namespace Netch
private static void Application_OnException(object sender, ThreadExceptionEventArgs e)
{
Log.Error(e.Exception, "未处理异常");
Log.Error(e.Exception, "Unhandled error");
}
private static void Application_OnExit(object? sender, EventArgs eventArgs)

View File

@@ -1,29 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\common.props" />
<PropertyGroup>
<OutputType>WinExe</OutputType>
<Configurations>Debug;Release</Configurations>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<StartupObject>Netch.Netch</StartupObject>
<ApplicationManifest>App.manifest</ApplicationManifest>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<ApplicationIcon>Resources\Netch.ico</ApplicationIcon>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<NoWarn>VSTHRD100</NoWarn>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<AnalysisMode>Default</AnalysisMode>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net5.0-windows</TargetFramework>
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
<Configurations>Debug;Release</Configurations>
<Platforms>x64</Platforms>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
@@ -38,59 +32,55 @@
<ItemGroup>
<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="Nullable.Extended.Analyzer" Version="1.2.4089">
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.72" 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.10.4539">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="4.1.2" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" Condition="'$(Configuration)'=='Debug'" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
<PackageReference Include="Stun.Net" Version="5.0.0" />
<PackageReference Include="System.Management" Version="5.0.0" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
<PackageReference Include="System.Text.Json" Version="6.0.0-rc.1.21451.13" />
<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="WindowsFirewallHelper" Version="2.1.4.81" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="5.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Update="Forms\Mode\RouteForm.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<None Remove="NativeMethods.txt" />
</ItemGroup>
<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,26 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace Netch.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@@ -15,12 +15,12 @@
"Stopping": "正在停止中",
"Stopped": "已停止",
"Starting {0}": "正在启动 {0}",
"Testing NAT": "正在测试 NAT",
"Testing NAT Type": "正在测试 NAT 类型",
"Setup Route Table Rule": "配置路由规则",
"Test failed": "测试失败",
"Starting update subscription": "正在更新订阅",
"Subscription updated": "订阅更新完毕",
"Register driver": "正在注册驱动",
"Updating servers": "正在更新服务器",
"Servers updated": "服务器更新完毕",
"Installing netfilter2 driver": "正在安装 netfilter2 驱动",
"Server": "服务器",
"Import Servers From Clipboard": "从剪贴板导入服务器",
@@ -28,10 +28,10 @@
"Netch is now minimized to the notification bar, double click this icon to restore.": "Netch 已最小化至通知栏,双击此图标恢复窗口",
"New version available": "发现新版本",
"Already latest version": "已经是最新版本",
"New version found failed": "寻找新版本失败",
"Check for update failed": "检查更新失败",
"Mode": "模式",
"Help": "帮助",
"Check for updates": "检查更新",
"Check for update": "检查更新",
"Download and install now?": "立即下载并安装?",
"Start downloading new version": "开始下载新版本",
"Download update failed": "下载更新错误",
@@ -64,16 +64,16 @@
"Plugin": "插件",
"Plugin Options": "插件参数",
"Subscribe": "订阅",
"Manage Subscribe Links": "管理订阅链接",
"Update Servers From Subscribe Links": "从订阅链接更新服务器",
"Subscription": "订阅",
"Manage Subscriptions": "管理订阅",
"Update Servers": "更新服务器",
"No subscription link": "没有任何一条订阅链接",
"Updating {0}": "正在更新 {0}",
"Update {1} server(s) from {0}": "从 {0} 更新 {1} 个服务器",
"Update servers error from {0}": "从 {0} 更新服务器失败",
"Update {1} server(s) from {0}": "从 {0} 更新 {1} 个服务器",
"Update servers failed from {0}": "从 {0} 更新服务器失败",
"Confirm deletion?": "确认删除?",
"DeleteServer": "删除订阅节点",
"CopyLink": "复制链接",
"Delete Servers": "删除订阅节点",
"Copy link": "复制链接",
"Status": "状态",
"Link": "链接",
"Unselect": "取消选择",
@@ -103,6 +103,7 @@
"Please select a mode first": "请先选择一个模式",
"Please enter a profile name first": "请先为该配置设置一个名称",
"No saved profile here. Save a profile first by Ctrl+Click on the button": "当前按钮下没有保存配置,请先使用 CTRL + 左键 点击该按钮保存一个配置",
"Lookup Server hostname failed": "解析服务器主机名失败",
"Used": "已使用",
"Testing": "测试中",
@@ -114,6 +115,9 @@
"Scan": "扫描",
"Save": "保存",
"Modify": "修改",
"Select": "选择",
"Validation": "验证",
"Action": "动作",
"Select a folder": "选择一个目录",
"Please enter an process name (xxx.exe)": "请输入一个进程名xxx.exe",
"Rule does not conform to C++ regular expression syntax": "规则不符合 C++ 正则表达式语法",
@@ -124,6 +128,7 @@
"Please enter a mode remark": "请输入模式的备注",
"File already exists.\n Please Change the filename": "文件名已存在,请修改文件名",
"Please enter a mode filename": "请输入模式的文件名",
"Above rules does not conform to C++ regular expression syntax": "以上规则不符合 C++ 正则表达式语法",
"Proxy Rule IPs": "代理规则 IP",
"Bypass Rule IPs": "绕过规则 IP",
@@ -132,6 +137,7 @@
"Delete or not ? Will clean up the corresponding group of items in the server list": "是否删除?将会清理服务器列表中对应组的项目",
"Remark can not be empty": "备注不可为空",
"Link can not be empty": "链接不可为空",
"Subscription with the specified remark already exists": "带有指定备注的订阅已存在",
"Link must start with http:// or https://": "链接必须以 http:// 或 https:// 开头",
"Settings": "设置",
@@ -154,15 +160,14 @@
"Handle process's DNS Hijack": "被代理进程 DNS 劫持",
"Child Process Handle": "子进程代理",
"ICMP Delay(ms)": "ICMP 延迟(毫秒)",
"Redirector built-in Shadowsocks support": "Redirector 内建 Shadowsocks 支持",
"Profile Count": "快捷配置数量",
"Delay test after start(sec)": "启动后延迟测试(秒)",
"Ping Protocol": "延迟测试协议",
"Detection Tick(sec)": "检测心跳(秒)",
"STUN Server": "STUN 服务器",
"Language": "语言",
"Resolve Server Hostname": "解析服务器主机名",
"FullCone Support (Required Server Xray-core v1.3.0+)": "FullCone 支持(需服务端 Xray-core v1.3.0+",
"Disable Support Warning": "停用支持警告",
"Profile": "配置名",
"Profiles": "配置",
@@ -172,5 +177,8 @@
"Exit": "退出",
"The {0} port is in use.": "{0} 端口已被占用",
"The {0} port is reserved by system.": "{0} 端口是系统保留端口"
"The {0} port is reserved by system.": "{0} 端口是系统保留端口",
"{0} won't get developers' support, Please do not report any issues or seek help from developers.": "{0} 将不会得到开发者的支持,请不要报告任何问题或寻求开发人员的帮助。",
"No Support": "不受支持"
}

View File

@@ -1,73 +0,0 @@
using System.Collections.Generic;
using System.Net;
using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models;
namespace Netch.Servers.Shadowsocks
{
public class SSController : Guard, IServerController
{
public SSController() : base("Shadowsocks.exe")
{
}
protected override IEnumerable<string> StartedKeywords => new[] { "listening at" };
protected override IEnumerable<string> FailedKeywords => new[] { "Invalid config path", "usage", "plugin service exit unexpectedly" };
public override string Name => "Shadowsocks";
public ushort? Socks5LocalPort { get; set; }
public string? LocalAddress { get; set; }
public Socks5 Start(in Server s)
{
var server = (Shadowsocks)s;
var command = new SSParameter
{
s = server.AutoResolveHostname(),
p = server.Port,
b = this.LocalAddress(),
l = this.Socks5LocalPort(),
m = server.EncryptMethod,
k = server.Password,
u = true,
plugin = server.Plugin,
plugin_opts = server.PluginOption
};
StartGuard(command.ToString());
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname);
}
[Verb]
private class SSParameter : ParameterBase
{
public string? s { get; set; }
public ushort? p { get; set; }
public string? b { get; set; }
public ushort? l { get; set; }
public string? m { get; set; }
public string? k { get; set; }
public bool u { get; set; }
[Full] [Optional] public string? plugin { get; set; }
[Full]
[Optional]
[RealName("plugin-opts")]
public string? plugin_opts { get; set; }
[Full] [Quote] [Optional] public string? acl { get; set; }
}
}
}

View File

@@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models;
namespace Netch.Servers
{
public class ShadowsocksController : Guard, IServerController
{
public ShadowsocksController() : base("Shadowsocks.exe")
{
}
protected override IEnumerable<string> StartedKeywords => new[] { "listening at" };
protected override IEnumerable<string> FailedKeywords => new[] { "Invalid config path", "usage", "plugin service exit unexpectedly" };
public override string Name => "Shadowsocks";
public ushort? Socks5LocalPort { get; set; }
public string? LocalAddress { get; set; }
public async Task<Socks5LocalServer> StartAsync(Server s)
{
var server = (ShadowsocksServer)s;
var arguments = new object?[]
{
"-s", await server.AutoResolveHostnameAsync(),
"-p", server.Port,
"-b", this.LocalAddress(),
"-l", this.Socks5LocalPort(),
"-m", server.EncryptMethod,
"-k", server.Password,
"-u", SpecialArgument.Flag,
"--plugin", server.Plugin,
"--plugin-opts", server.PluginOption
};
await StartGuardAsync(Arguments.Format(arguments));
return new Socks5LocalServer(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname);
}
}
}

View File

@@ -1,13 +1,13 @@
using Netch.Forms;
using Netch.Utils;
namespace Netch.Servers.Shadowsocks.Form
namespace Netch.Servers
{
public class ShadowsocksForm : ServerForm
{
public ShadowsocksForm(Shadowsocks? server = default)
public ShadowsocksForm(ShadowsocksServer? server = default)
{
server ??= new Shadowsocks();
server ??= new ShadowsocksServer();
Server = server;
CreateTextBox("Password", "Password", s => !s.IsNullOrWhiteSpace(), s => server.Password = s, server.Password);
CreateComboBox("EncryptMethod", "Encrypt Method", SSGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod);

View File

@@ -1,9 +1,9 @@
using System.Collections.Generic;
using Netch.Models;
namespace Netch.Servers.Shadowsocks
namespace Netch.Servers
{
public class Shadowsocks : Server
public class ShadowsocksServer : Server
{
public override string Type { get; } = "SS";
public override string MaskedData()

View File

@@ -6,14 +6,12 @@ using System.Text.RegularExpressions;
using System.Web;
using Netch.Interfaces;
using Netch.Models;
using Netch.Servers.Shadowsocks.Form;
using Netch.Servers.Shadowsocks.Models.SSD;
using Netch.Utils;
using Serilog;
namespace Netch.Servers.Shadowsocks
namespace Netch.Servers
{
public class SSUtil : IServerUtil
public class ShadowsocksUtil : IServerUtil
{
public ushort Priority { get; } = 1;
@@ -25,11 +23,11 @@ namespace Netch.Servers.Shadowsocks
public string[] UriScheme { get; } = { "ss", "ssd" };
public Type ServerType { get; } = typeof(Shadowsocks);
public Type ServerType { get; } = typeof(ShadowsocksServer);
public void Edit(Server s)
{
new ShadowsocksForm((Shadowsocks)s).ShowDialog();
new ShadowsocksForm((ShadowsocksServer)s).ShowDialog();
}
public void Create()
@@ -39,7 +37,7 @@ namespace Netch.Servers.Shadowsocks
public string GetShareLink(Server s)
{
var server = (Shadowsocks)s;
var server = (ShadowsocksServer)s;
// ss://method:password@server:port#Remark
return "ss://" + ShareLink.URLSafeBase64Encode($"{server.EncryptMethod}:{server.Password}@{server.Hostname}:{server.Port}") + "#" +
HttpUtility.UrlEncode(server.Remark);
@@ -47,7 +45,7 @@ namespace Netch.Servers.Shadowsocks
public IServerController GetController()
{
return new SSController();
return new ShadowsocksController();
}
public IEnumerable<Server> ParseUri(string text)
@@ -63,10 +61,10 @@ namespace Netch.Servers.Shadowsocks
public bool CheckServer(Server s)
{
var server = (Shadowsocks)s;
var server = (ShadowsocksServer)s;
if (!SSGlobal.EncryptMethods.Contains(server.EncryptMethod))
{
Log.Warning("不支持的 SS 加密方式:{Method}", server.EncryptMethod);
Log.Warning("Unsupported SS Encrypt Method: {Method}", server.EncryptMethod);
return false;
}
@@ -75,9 +73,9 @@ namespace Netch.Servers.Shadowsocks
public IEnumerable<Server> ParseSsdUri(string s)
{
var json = JsonSerializer.Deserialize<Main>(ShareLink.URLSafeBase64Decode(s.Substring(6)))!;
var json = JsonSerializer.Deserialize<SSDJObject>(ShareLink.URLSafeBase64Decode(s.Substring(6)))!;
return json.servers.Select(server => new Shadowsocks
return json.servers.Select(server => new ShadowsocksServer
{
Remark = server.remarks,
Hostname = server.server,
@@ -92,9 +90,9 @@ namespace Netch.Servers.Shadowsocks
.Where(CheckServer);
}
public Shadowsocks ParseSsUri(string text)
public ShadowsocksServer ParseSsUri(string text)
{
var data = new Shadowsocks();
var data = new ShadowsocksServer();
text = text.Replace("/?", "?");
if (text.Contains("#"))

View File

@@ -1,9 +1,9 @@
#nullable disable
using System.Collections.Generic;
namespace Netch.Servers.Shadowsocks.Models.SSD
namespace Netch.Servers
{
public class Main
public class SSDJObject
{
/// <summary>
/// 机场名
@@ -38,6 +38,6 @@ namespace Netch.Servers.Shadowsocks.Models.SSD
/// <summary>
/// 服务器数组
/// </summary>
public List<SSDServer> servers;
public List<SSDServerJObject> servers;
}
}

View File

@@ -1,7 +1,7 @@
#nullable disable
namespace Netch.Servers.Shadowsocks.Models.SSD
namespace Netch.Servers
{
public class SSDServer
public class SSDServerJObject
{
/// <summary>
/// 加密方式

View File

@@ -1,6 +1,10 @@
#nullable disable
namespace Netch.Servers.Shadowsocks.Models
namespace Netch.Servers
{
/// <summary>
/// Import Shadowsocks Server from Json Configuration
/// <see cref="Utils.ShareLink.ParseText"/>
/// </summary>
public class ShadowsocksConfig
{
public string server { get; set; }

View File

@@ -1,87 +0,0 @@
using System.Collections.Generic;
using System.Net;
using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models;
namespace Netch.Servers.ShadowsocksR
{
public class SSRController : Guard, IServerController
{
public SSRController() : base("ShadowsocksR.exe")
{
}
protected override IEnumerable<string> StartedKeywords => new[] { "listening at" };
protected override IEnumerable<string> FailedKeywords => new[] { "Invalid config path", "usage" };
public override string Name => "ShadowsocksR";
public ushort? Socks5LocalPort { get; set; }
public string? LocalAddress { get; set; }
public Socks5 Start(in Server s)
{
var server = (ShadowsocksR)s;
var command = new SSRParameter
{
s = server.AutoResolveHostname(),
p = server.Port,
k = server.Password,
m = server.EncryptMethod,
t = "120",
O = server.Protocol,
G = server.ProtocolParam,
o = server.OBFS,
g = server.OBFSParam,
b = this.LocalAddress(),
l = this.Socks5LocalPort(),
u = true
};
StartGuard(command.ToString());
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(),server.Hostname);
}
[Verb]
class SSRParameter : ParameterBase
{
public string? s { get; set; }
public ushort? p { get; set; }
[Quote]
public string? k { get; set; }
public string? m { get; set; }
public string? t { get; set; }
[Optional]
public string? O { get; set; }
[Optional]
public string? G { get; set; }
[Optional]
public string? o { get; set; }
[Optional]
public string? g { get; set; }
public string? b { get; set; }
public ushort? l { get; set; }
public bool u { get; set; }
[Full]
[Quote]
[Optional]
public string? acl { get; set; }
}
}
}

View File

@@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models;
namespace Netch.Servers
{
public class ShadowsocksRController : Guard, IServerController
{
public ShadowsocksRController() : base("ShadowsocksR.exe")
{
}
protected override IEnumerable<string> StartedKeywords => new[] { "listening at" };
protected override IEnumerable<string> FailedKeywords => new[] { "Invalid config path", "usage" };
public override string Name => "ShadowsocksR";
public ushort? Socks5LocalPort { get; set; }
public string? LocalAddress { get; set; }
public async Task<Socks5LocalServer> StartAsync(Server s)
{
var server = (ShadowsocksRServer)s;
var arguments = new object?[]
{
"-s", await server.AutoResolveHostnameAsync(),
"-p", server.Port,
"-k", server.Password,
"-m", server.EncryptMethod,
"-t", 120,
"-O", server.Protocol,
"-G", server.ProtocolParam,
"-o", server.OBFS,
"-g", server.OBFSParam,
"-b", this.LocalAddress(),
"-l", this.Socks5LocalPort(),
"-u", SpecialArgument.Flag
};
await StartGuardAsync(Arguments.Format(arguments));
return new Socks5LocalServer(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname);
}
}
}

View File

@@ -1,13 +1,13 @@
using Netch.Forms;
using Netch.Utils;
namespace Netch.Servers.ShadowsocksR.Form
namespace Netch.Servers
{
public class ShadowsocksRForm : ServerForm
{
public ShadowsocksRForm(ShadowsocksR? server = default)
public ShadowsocksRForm(ShadowsocksRServer? server = default)
{
server ??= new ShadowsocksR();
server ??= new ShadowsocksRServer();
Server = server;
CreateTextBox("Password", "Password", s => !s.IsNullOrWhiteSpace(), s => server.Password = s, server.Password);
CreateComboBox("EncryptMethod", "Encrypt Method", SSRGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod);

View File

@@ -1,9 +1,9 @@
using System.Collections.Generic;
using Netch.Models;
namespace Netch.Servers.ShadowsocksR
namespace Netch.Servers
{
public class ShadowsocksR : Server
public class ShadowsocksRServer : Server
{
public override string Type { get; } = "SSR";
public override string MaskedData()

View File

@@ -3,14 +3,12 @@ using System.Collections.Generic;
using System.Text.RegularExpressions;
using Netch.Interfaces;
using Netch.Models;
using Netch.Servers.Shadowsocks;
using Netch.Servers.ShadowsocksR.Form;
using Netch.Utils;
using Serilog;
namespace Netch.Servers.ShadowsocksR
namespace Netch.Servers
{
public class SSRUtil : IServerUtil
public class ShadowsocksRUtil : IServerUtil
{
public ushort Priority { get; } = 1;
@@ -22,11 +20,11 @@ namespace Netch.Servers.ShadowsocksR
public string[] UriScheme { get; } = { "ssr" };
public Type ServerType { get; } = typeof(ShadowsocksR);
public Type ServerType { get; } = typeof(ShadowsocksRServer);
public void Edit(Server s)
{
new ShadowsocksRForm((ShadowsocksR)s).ShowDialog();
new ShadowsocksRForm((ShadowsocksRServer)s).ShowDialog();
}
public void Create()
@@ -36,7 +34,7 @@ namespace Netch.Servers.ShadowsocksR
public string GetShareLink(Server s)
{
var server = (ShadowsocksR)s;
var server = (ShadowsocksRServer)s;
// https://github.com/shadowsocksr-backup/shadowsocks-rss/wiki/SSR-QRcode-scheme
// ssr://base64(host:port:protocol:method:obfs:base64pass/?obfsparam=base64param&protoparam=base64param&remarks=base64remarks&group=base64group&udpport=0&uot=0)
@@ -50,7 +48,7 @@ namespace Netch.Servers.ShadowsocksR
public IServerController GetController()
{
return new SSRController();
return new ShadowsocksRController();
}
/// <summary>
@@ -113,7 +111,7 @@ namespace Netch.Servers.ShadowsocksR
if (SSGlobal.EncryptMethods.Contains(method) && protocol == "origin" && obfs == "plain")
return new[]
{
new Shadowsocks.Shadowsocks
new ShadowsocksServer
{
Hostname = serverAddr,
Port = serverPort,
@@ -126,7 +124,7 @@ namespace Netch.Servers.ShadowsocksR
return new[]
{
new ShadowsocksR
new ShadowsocksRServer
{
Hostname = serverAddr,
Port = serverPort,
@@ -144,22 +142,22 @@ namespace Netch.Servers.ShadowsocksR
public bool CheckServer(Server s)
{
var server = (ShadowsocksR)s;
var server = (ShadowsocksRServer)s;
if (!SSRGlobal.EncryptMethods.Contains(server.EncryptMethod))
{
Log.Error("不支持的 SSR 加密方式:{Method}", server.EncryptMethod);
Log.Error("Unsupported ShadowsocksR Encrypt method: {Method}", server.EncryptMethod);
return false;
}
if (!SSRGlobal.Protocols.Contains(server.Protocol))
{
Log.Error("不支持的 SSR 协议:{Protocol}", server.Protocol);
Log.Error("Unsupported ShadowsocksR Protocol: {Protocol}", server.Protocol);
return false;
}
if (!SSRGlobal.OBFSs.Contains(server.OBFS))
{
Log.Error("不支持的 SSR 混淆:{Obfs}", server.OBFS);
Log.Error("Unsupported ShadowsocksR Obfs: {Obfs}", server.OBFS);
return false;
}

View File

@@ -1,19 +0,0 @@
using Netch.Models;
using Netch.Servers;
namespace Netch.Servers
{
public class S5Controller : V2rayController
{
public override string Name { get; } = "Socks5";
public override Socks5 Start(in Server s)
{
var server = (Socks5)s;
if (server.Auth())
base.Start(s);
return server;
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Threading.Tasks;
using Netch.Models;
namespace Netch.Servers
{
public class Socks5Controller : V2rayController
{
public override string Name { get; } = "Socks5";
public override async Task<Socks5LocalServer> StartAsync(Server s)
{
var server = (Socks5Server)s;
if (!server.Auth())
throw new ArgumentException();
return await base.StartAsync(s);
}
}
}

View File

@@ -4,9 +4,9 @@ namespace Netch.Servers
{
public class Socks5Form : ServerForm
{
public Socks5Form(Socks5? server = default)
public Socks5Form(Socks5Server? server = default)
{
server ??= new Socks5();
server ??= new Socks5Server();
Server = server;
CreateTextBox("Username", "Username", s => true, s => server.Username = s, server.Username);
CreateTextBox("Password", "Password", s => true, s => server.Password = s, server.Password);

View File

@@ -5,10 +5,12 @@
/// Encrypted proxy client's local socks5 server
/// (<see cref="RemoteHostname"/> property is used for saving remote address/hostname for special use)
/// </summary>
public class Socks5Bridge : Socks5
public class Socks5LocalServer : Socks5Server
{
public Socks5Bridge(string hostname, ushort port, string remoteHostname) : base(hostname, port)
public Socks5LocalServer(string hostname, ushort port, string remoteHostname)
{
Hostname = hostname;
Port = port;
RemoteHostname = remoteHostname;
}

View File

@@ -2,17 +2,17 @@
namespace Netch.Servers
{
public class Socks5 : Server
public class Socks5Server : Server
{
/// <summary>
/// 密码
/// </summary>
public string? Password;
public string? Password { get; set; }
/// <summary>
/// 账号
/// </summary>
public string? Username;
public string? Username { get; set; }
public override string Type { get; } = "Socks5";
@@ -21,17 +21,17 @@ namespace Netch.Servers
return $"Auth: {Auth()}";
}
public Socks5()
public Socks5Server()
{
}
public Socks5(string hostname, ushort port)
public Socks5Server(string hostname, ushort port)
{
Hostname = hostname;
Port = port;
}
public Socks5(string hostname, ushort port, string username, string password) : this(hostname, port)
public Socks5Server(string hostname, ushort port, string username, string password) : this(hostname, port)
{
Username = username;
Password = password;

View File

@@ -6,7 +6,7 @@ using Netch.Models;
namespace Netch.Servers
{
public class S5Util : IServerUtil
public class Socks5Util : IServerUtil
{
public ushort Priority { get; } = 0;
@@ -18,11 +18,11 @@ namespace Netch.Servers
public string[] UriScheme { get; } = { };
public Type ServerType { get; } = typeof(Socks5);
public Type ServerType { get; } = typeof(Socks5Server);
public void Edit(Server s)
{
new Socks5Form((Socks5)s).ShowDialog();
new Socks5Form((Socks5Server)s).ShowDialog();
}
public void Create()
@@ -32,7 +32,7 @@ namespace Netch.Servers
public string GetShareLink(Server s)
{
var server = (Socks5)s;
var server = (Socks5Server)s;
// https://t.me/socks?server=1.1.1.1&port=443
return $"https://t.me/socks?server={server.Hostname}&port={server.Port}" +
$"{(!string.IsNullOrWhiteSpace(server.Username) ? $"&user={server.Username}" : "")}" +
@@ -41,7 +41,7 @@ namespace Netch.Servers
public IServerController GetController()
{
return new S5Controller();
return new Socks5Controller();
}
public IEnumerable<Server> ParseUri(string text)
@@ -55,7 +55,7 @@ namespace Netch.Servers
if (!dict.ContainsKey("server") || !dict.ContainsKey("port"))
throw new FormatException();
var data = new Socks5
var data = new Socks5Server
{
Hostname = dict["server"],
Port = ushort.Parse(dict["port"])

View File

@@ -1,7 +1,7 @@
#nullable disable
using System.Collections.Generic;
namespace Netch.Servers.Models
namespace Netch.Servers
{
public class TrojanConfig
{

View File

@@ -2,10 +2,10 @@
using System.IO;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using Netch.Controllers;
using Netch.Interfaces;
using Netch.Models;
using Netch.Servers.Models;
using Netch.Utils;
namespace Netch.Servers
@@ -26,14 +26,14 @@ namespace Netch.Servers
public string? LocalAddress { get; set; }
public Socks5 Start(in Server s)
public async Task<Socks5LocalServer> StartAsync(Server s)
{
var server = (Trojan)s;
var server = (TrojanServer)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>
{
@@ -41,17 +41,17 @@ namespace Netch.Servers
},
ssl = new TrojanSSL
{
sni = server.Host.ValueOrDefault() ?? (Global.Settings.ResolveServerHostname ? server.Hostname : "")
sni = server.Host.ValueOrDefault() ?? server.Hostname
}
};
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");
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname);
await StartGuardAsync("-c ..\\data\\last.json");
return new Socks5LocalServer(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname);
}
}
}

View File

@@ -1,12 +1,12 @@
using Netch.Forms;
namespace Netch.Servers.Form
namespace Netch.Servers
{
public class TrojanForm : ServerForm
{
public TrojanForm(Trojan? server = default)
public TrojanForm(TrojanServer? server = default)
{
server ??= new Trojan();
server ??= new TrojanServer();
Server = server;
CreateTextBox("Password", "Password", s => true, s => server.Password = s, server.Password);
CreateTextBox("Host", "Host", s => true, s => server.Host = s, server.Host);

View File

@@ -2,7 +2,7 @@ using Netch.Models;
namespace Netch.Servers
{
public class Trojan : Server
public class TrojanServer : Server
{
public override string Type { get; } = "Trojan";
public override string MaskedData()

View File

@@ -4,7 +4,6 @@ using System.Text.RegularExpressions;
using System.Web;
using Netch.Interfaces;
using Netch.Models;
using Netch.Servers.Form;
namespace Netch.Servers
{
@@ -20,11 +19,11 @@ namespace Netch.Servers
public string[] UriScheme { get; } = { "trojan" };
public Type ServerType { get; } = typeof(Trojan);
public Type ServerType { get; } = typeof(TrojanServer);
public void Edit(Server s)
{
new TrojanForm((Trojan)s).ShowDialog();
new TrojanForm((TrojanServer)s).ShowDialog();
}
public void Create()
@@ -34,7 +33,7 @@ namespace Netch.Servers
public string GetShareLink(Server s)
{
var server = (Trojan)s;
var server = (TrojanServer)s;
return $"trojan://{HttpUtility.UrlEncode(server.Password)}@{server.Hostname}:{server.Port}#{server.Remark}";
}
@@ -45,7 +44,7 @@ namespace Netch.Servers
public IEnumerable<Server> ParseUri(string text)
{
var data = new Trojan();
var data = new TrojanServer();
text = text.Replace("/?", "?");
if (text.Contains("#"))

View File

@@ -1,9 +1,6 @@
namespace Netch.Servers.Models
namespace Netch.Servers
{
/// <summary>
/// 使用 v2rayN 定义的 VMess 链接格式
/// </summary>
public class V2rayNSharing
public class V2rayNJObject
{
/// <summary>
/// 链接版本

View File

@@ -1,7 +1,7 @@
#nullable disable
// ReSharper disable InconsistentNaming
namespace Netch.Servers.V2ray.Models
namespace Netch.Servers
{
public struct V2rayConfig
{

View File

@@ -1,13 +1,14 @@
using Netch.Models;
using Netch.Servers.V2ray.Models;
using System.Threading.Tasks;
using Netch.Models;
using Netch.Utils;
using V2rayConfig = Netch.Servers.V2ray.Models.V2rayConfig;
namespace Netch.Servers.Utils
#pragma warning disable VSTHRD200
namespace Netch.Servers
{
public static class V2rayConfigUtils
{
public static V2rayConfig GenerateClientConfig(Server server)
public static async Task<V2rayConfig> GenerateClientConfigAsync(Server server)
{
var v2rayConfig = new V2rayConfig
{
@@ -26,12 +27,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
{
@@ -41,14 +42,14 @@ namespace Netch.Servers.Utils
switch (server)
{
case Socks5 socks5:
case Socks5Server socks5:
{
outbound.protocol = "socks";
outbound.settings.servers = new object[]
{
new
{
address = server.AutoResolveHostname(),
address = await server.AutoResolveHostnameAsync(),
port = server.Port,
users = socks5.Auth()
? new[]
@@ -68,14 +69,14 @@ namespace Netch.Servers.Utils
outbound.mux.concurrency = -1;
break;
}
case VLESS vless:
case VLESSServer vless:
{
outbound.protocol = "vless";
outbound.settings.vnext = new[]
{
new VnextItem
{
address = server.AutoResolveHostname(),
address = await server.AutoResolveHostnameAsync(),
port = server.Port,
users = new[]
{
@@ -104,14 +105,14 @@ namespace Netch.Servers.Utils
break;
}
case VMess vmess:
case VMessServer vmess:
{
outbound.protocol = "vmess";
outbound.settings.vnext = new[]
{
new VnextItem
{
address = server.AutoResolveHostname(),
address = await server.AutoResolveHostnameAsync(),
port = server.Port,
users = new[]
{
@@ -136,7 +137,7 @@ namespace Netch.Servers.Utils
return outbound;
}
private static StreamSettings boundStreamSettings(VMess server)
private static StreamSettings boundStreamSettings(VMessServer server)
{
// https://xtls.github.io/config/transports
@@ -151,7 +152,7 @@ namespace Netch.Servers.Utils
var tlsSettings = new TlsSettings
{
allowInsecure = Global.Settings.V2RayConfig.AllowInsecure,
serverName = server.ServerName.ValueOrDefault() ?? server.Hostname
serverName = server.ServerName.ValueOrDefault() ?? server.Host.SplitOrDefault()?[0]
};
switch (server.TLSSecureType)

View File

@@ -2,10 +2,10 @@ 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;
using Netch.Servers.Utils;
namespace Netch.Servers
{
@@ -27,15 +27,15 @@ namespace Netch.Servers
public string? LocalAddress { get; set; }
public virtual Socks5 Start(in Server s)
public virtual async Task<Socks5LocalServer> 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");
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), s.Hostname);
await StartGuardAsync("-config ..\\data\\last.json");
return new Socks5LocalServer(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), s.Hostname);
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Netch.Servers
public static IEnumerable<Server> ParseVUri(string text)
{
var scheme = ShareLink.GetUriScheme(text).ToLower();
var server = scheme switch { "vmess" => new VMess(), "vless" => new VLESS(), _ => throw new ArgumentOutOfRangeException() };
var server = scheme switch { "vmess" => new VMessServer(), "vless" => new VLESSServer(), _ => throw new ArgumentOutOfRangeException() };
if (text.Contains("#"))
{
server.Remark = Uri.UnescapeDataString(text.Split('#')[1]);
@@ -58,7 +58,7 @@ namespace Netch.Servers
{
server.ServerName = parameter.Get("sni") ?? "";
if (server.TLSSecureType == "xtls")
((VLESS)server).Flow = parameter.Get("flow") ?? "";
((VLESSServer)server).Flow = parameter.Get("flow") ?? "";
}
}
@@ -77,7 +77,7 @@ namespace Netch.Servers
public static string GetVShareLink(Server s, string scheme = "vmess")
{
// https://github.com/XTLS/Xray-core/issues/91
var server = (VMess)s;
var server = (VMessServer)s;
var parameter = new Dictionary<string, string>();
// protocol-specific fields
parameter.Add("type", server.TransferProtocol);
@@ -138,7 +138,7 @@ namespace Netch.Servers
if (server.TLSSecureType == "xtls")
{
var flow = ((VLESS)server).Flow;
var flow = ((VLESSServer)server).Flow;
if (!flow.IsNullOrWhiteSpace())
parameter.Add("flow", flow!.Replace("-udp443", ""));
}

View File

@@ -1,13 +1,13 @@
using System.Collections.Generic;
using Netch.Forms;
namespace Netch.Servers.VLESSForm
namespace Netch.Servers
{
internal class VLESSForm : ServerForm
{
public VLESSForm(VLESS? server = default)
public VLESSForm(VLESSServer? server = default)
{
server ??= new VLESS();
server ??= new VLESSServer();
Server = server;
CreateTextBox("Sni", "ServerName(Sni)", s => true, s => server.ServerName = s, server.ServerName);
CreateTextBox("UUID", "UUID", s => true, s => server.UserID = s, server.UserID);

View File

@@ -2,7 +2,7 @@ using System.Collections.Generic;
namespace Netch.Servers
{
public class VLESS : VMess
public class VLESSServer : VMessServer
{
public override string Type { get; } = "VLESS";

View File

@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using Netch.Interfaces;
using Netch.Models;
using Netch.Servers;
namespace Netch.Servers
{
@@ -18,16 +17,16 @@ namespace Netch.Servers
public string[] UriScheme { get; } = { "vless" };
public Type ServerType { get; } = typeof(VLESS);
public Type ServerType { get; } = typeof(VLESSServer);
public void Edit(Server s)
{
new VLESSForm.VLESSForm((VLESS)s).ShowDialog();
new VLESSForm((VLESSServer)s).ShowDialog();
}
public void Create()
{
new VLESSForm.VLESSForm().ShowDialog();
new VLESSForm().ShowDialog();
}
public string GetShareLink(Server s)

View File

@@ -1,13 +1,13 @@
using System.Collections.Generic;
using Netch.Forms;
namespace Netch.Servers.Form
namespace Netch.Servers
{
public class VMessForm : ServerForm
{
public VMessForm(VMess? server = default)
public VMessForm(VMessServer? server = default)
{
server ??= new VMess();
server ??= new VMessServer();
Server = server;
CreateTextBox("Sni", "ServerName(Sni)", s => true, s => server.ServerName = s, server.ServerName);
CreateTextBox("UserId", "User ID", s => true, s => server.UserID = s, server.UserID);

View File

@@ -3,7 +3,7 @@ using Netch.Models;
namespace Netch.Servers
{
public class VMess : Server
public class VMessServer : Server
{
private string _tlsSecureType = VMessGlobal.TLSSecure[0];

View File

@@ -5,8 +5,6 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Netch.Interfaces;
using Netch.Models;
using Netch.Servers.Form;
using Netch.Servers.Models;
using Netch.Utils;
namespace Netch.Servers
@@ -23,11 +21,11 @@ namespace Netch.Servers
public string[] UriScheme { get; } = { "vmess" };
public Type ServerType { get; } = typeof(VMess);
public Type ServerType { get; } = typeof(VMessServer);
public void Edit(Server s)
{
new VMessForm((VMess)s).ShowDialog();
new VMessForm((VMessServer)s).ShowDialog();
}
public void Create()
@@ -39,9 +37,9 @@ namespace Netch.Servers
{
if (Global.Settings.V2RayConfig.V2rayNShareLink)
{
var server = (VMess)s;
var server = (VMessServer)s;
var vmessJson = JsonSerializer.Serialize(new V2rayNSharing
var vmessJson = JsonSerializer.Serialize(new V2rayNJObject
{
v = 2,
ps = server.Remark,
@@ -75,7 +73,7 @@ namespace Netch.Servers
public IEnumerable<Server> ParseUri(string text)
{
var data = new VMess();
var data = new VMessServer();
string s;
try
@@ -87,7 +85,7 @@ namespace Netch.Servers
return V2rayUtils.ParseVUri(text);
}
V2rayNSharing vmess = JsonSerializer.Deserialize<V2rayNSharing>(s,
V2rayNJObject vmess = JsonSerializer.Deserialize<V2rayNJObject>(s,
new JsonSerializerOptions { NumberHandling = JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowReadingFromString })!;
data.Remark = vmess.ps;

View File

@@ -5,8 +5,9 @@ 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 Netch.Enums;
using Serilog;
namespace Netch.Utils
@@ -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("Net traffic processes: {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,8 @@ using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Threading;
using Netch.JsonConverter;
using Netch.Models;
using Serilog;
@@ -24,6 +26,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,36 +46,39 @@ 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);
Log.Information("Load backup configuration \"{FileName}\"", BackupFileFullName);
await LoadCoreAsync(BackupFileFullName);
}
catch (Exception e)
{
Log.Error(e, "加载配置异常");
Log.Error(e, "Load configuration failed");
Environment.Exit(-1);
}
}
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)
{
Log.Error(e, @"从 {FileName} 加载配置异常", filename);
Log.Error(e, "Load configuration file \"{FileName}\" error ", filename);
return false;
}
}
@@ -84,8 +91,8 @@ namespace Netch.Utils
for (var i = 0; i < settings.Profiles.Count; i++)
settings.Profiles[i].Index = i;
settings.AioDNS.ChinaDNS = Utils.HostAppendPort(settings.AioDNS.ChinaDNS);
settings.AioDNS.OtherDNS = Utils.HostAppendPort(settings.AioDNS.OtherDNS);
settings.AioDNS.ChinaDNS = DnsUtils.AppendPort(settings.AioDNS.ChinaDNS);
settings.AioDNS.OtherDNS = DnsUtils.AppendPort(settings.AioDNS.OtherDNS);
}
/// <summary>
@@ -93,23 +100,38 @@ 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 (fileStream.ConfigureAwait(false))
await using (var fileStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true))
{
await JsonSerializer.SerializeAsync(fileStream, Global.Settings, JsonSerializerOptions).ConfigureAwait(false);
await JsonSerializer.SerializeAsync(fileStream, Global.Settings, JsonSerializerOptions);
}
await EnsureConfigFileExistsAsync();
File.Replace(tempFile, FileFullName, BackupFileFullName);
}
catch (Exception e)
{
Log.Error(e, "保存配置异常");
Log.Error(e, "Save Configuration error");
}
}
private static async ValueTask EnsureConfigFileExistsAsync()
{
if (!File.Exists(FileFullName))
{
await File.Create(FileFullName).DisposeAsync();
}
}
}

View File

@@ -0,0 +1,95 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using Microsoft.VisualStudio.Threading;
using Netch.Models;
namespace Netch.Utils
{
public static class DelayTestHelper
{
private static readonly Timer Timer;
private static readonly AsyncSemaphore Lock = new(1);
private static readonly AsyncSemaphore PoolLock = new(16);
public static readonly NumberRange Range = new(0, int.MaxValue / 1000);
private static bool _enabled = true;
static DelayTestHelper()
{
Timer = new Timer
{
Interval = 10000,
AutoReset = true
};
Timer.Elapsed += (_, _) => PerformTestAsync().Forget();
}
public static bool Enabled
{
get => _enabled;
set
{
_enabled = value;
UpdateTick();
}
}
/// <param name="waitFinish">if does not get lock, block until last release</param>
public static async Task PerformTestAsync(bool waitFinish = false)
{
if (Lock.CurrentCount == 0)
{
if (waitFinish)
(await Lock.EnterAsync()).Dispose();
return;
}
using var _ = await Lock.EnterAsync();
try
{
var tasks = Global.Settings.Server.Select(async s =>
{
using (await PoolLock.EnterAsync())
{
await s.PingAsync();
}
});
await Task.WhenAll(tasks);
}
catch (Exception)
{
// ignored
}
}
public static void UpdateTick(bool performTestAtOnce = false)
{
UpdateTick(Global.Settings.DetectionTick, performTestAtOnce);
}
/// <param name="interval">interval(seconds), 0 disable, MaxValue <c>int.MaxValue/1000</c></param>
/// <param name="performTestAtOnce"></param>
private static void UpdateTick(int interval, bool performTestAtOnce = false)
{
Timer.Stop();
var enable = Enabled && interval > 0 && Range.InRange(interval);
if (enable)
{
Timer.Interval = interval * 1000;
Timer.Start();
if (performTestAtOnce)
PerformTestAsync().Forget();
}
}
}
}

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,31 +16,64 @@ 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.Unspecified, int timeout = 3000)
{
try
{
if (Cache.Contains(hostname))
return Cache[hostname] as IPAddress;
var cacheResult = inet switch
{
AddressFamily.Unspecified => (IPAddress?)(Cache[hostname] ?? Cache6[hostname]),
AddressFamily.InterNetwork => (IPAddress?)Cache[hostname],
AddressFamily.InterNetworkV6 => (IPAddress?)Cache6[hostname],
_ => throw new ArgumentOutOfRangeException()
};
var task = Dns.GetHostAddressesAsync(hostname);
if (!task.Wait(timeout))
return null;
if (cacheResult != null)
return cacheResult;
if (task.Result.Length == 0)
return null;
Cache.Add(hostname, task.Result[0]);
return task.Result[0];
return await LookupNoCacheAsync(hostname, inet, timeout);
}
catch (Exception)
catch (Exception e)
{
Log.Verbose(e, "Lookup hostname {Hostname} failed", hostname);
return null;
}
}
private static async Task<IPAddress?> LookupNoCacheAsync(string hostname, AddressFamily inet = AddressFamily.Unspecified, int timeout = 3000)
{
using var task = Dns.GetHostAddressesAsync(hostname);
using var resTask = await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false);
if (resTask == task)
{
var addresses = await task;
var result = addresses.FirstOrDefault(i => inet == AddressFamily.Unspecified || inet == i.AddressFamily);
if (result == null)
return null;
switch (result.AddressFamily)
{
case AddressFamily.InterNetwork:
Cache.Add(hostname, result);
break;
case AddressFamily.InterNetworkV6:
Cache6.Add(hostname, result);
break;
default:
Trace.Assert(false);
break;
}
return result;
}
return null;
}
/// <summary>
/// 查询
/// </summary>
@@ -45,6 +82,7 @@ namespace Netch.Utils
public static void ClearCache()
{
Cache.Clear();
Cache6.Clear();
}
public static IEnumerable<string> Split(string dns)
@@ -63,5 +101,21 @@ namespace Netch.Utils
{
return string.Join(",", dns);
}
public static string AppendPort(string host, ushort port = 53)
{
if (!host.Contains(':'))
return host + $":{port}";
return host;
}
public static string AppendScheme(string value, string scheme = "tcp")
{
if (!value.Contains(Uri.SchemeDelimiter))
return scheme + Uri.SchemeDelimiter + value;
return value;
}
}
}

View File

@@ -1,9 +1,9 @@
using System;
using System.IO;
using System.Linq;
using Serilog;
using WindowsFirewallHelper;
using WindowsFirewallHelper.FirewallRules;
using Serilog;
namespace Netch.Utils
{
@@ -16,9 +16,9 @@ namespace Netch.Utils
/// </summary>
public static void AddNetchFwRules()
{
if (!FirewallWAS.IsSupported)
if (!FirewallWAS.IsLocallySupported)
{
Log.Warning("不支持防火墙");
Log.Warning("Windows Firewall Locally Unsupported");
return;
}
@@ -38,7 +38,7 @@ namespace Netch.Utils
}
catch (Exception e)
{
Log.Warning(e, "添加防火墙规则错误");
Log.Warning(e, "Create Netch Firewall rules error");
}
}
@@ -47,7 +47,7 @@ namespace Netch.Utils
/// </summary>
public static void RemoveNetchFwRules()
{
if (!FirewallWAS.IsSupported)
if (!FirewallWAS.IsLocallySupported)
return;
try
@@ -58,7 +58,7 @@ namespace Netch.Utils
}
catch (Exception e)
{
Log.Warning(e, "清除防火墙规则错误");
Log.Warning(e, "Remove Netch Firewall rules error");
}
}

View File

@@ -6,8 +6,6 @@ using Netch.Controllers;
using Netch.Enums;
using Netch.Interfaces;
using Netch.Models;
using Netch.Servers;
using Netch.Servers.Shadowsocks;
using Serilog;
namespace Netch.Utils
@@ -126,41 +124,23 @@ namespace Netch.Utils
File.Delete(mode.FullName);
}
public static bool SkipServerController(Server server, Mode mode)
{
switch (mode.Type)
{
case ModeType.Process:
return server switch
{
Socks5 => true,
Shadowsocks shadowsocks when !shadowsocks.HasPlugin() && Global.Settings.Redirector.RedirectorSS => true,
_ => false
};
case ModeType.ProxyRuleIPs:
case ModeType.BypassRuleIPs:
return server is Socks5;
default:
return false;
}
}
public static IModeController GetModeControllerByType(ModeType type, out ushort? port, out string portName)
public static (IModeController, ModeFeature) GetModeControllerByType(ModeType type, out ushort? port, out string portName)
{
port = null;
portName = string.Empty;
switch (type)
{
case ModeType.Process:
return new NFController();
return (new NFController(), ModeFeature.SupportIPv6 | ModeFeature.SupportSocks5Auth);
case ModeType.ProxyRuleIPs:
return (new TUNController(), ModeFeature.SupportSocks5Auth);
case ModeType.BypassRuleIPs:
return new TUNController();
return (new TUNController(), ModeFeature.SupportSocks5Auth);
case ModeType.Pcap2Socks:
return new PcapController();
return (new PcapController(), 0);
default:
Log.Error("未知模式类型");
throw new MessageException("未知模式类型");
Log.Error("Unknown Mode Type \"{Type}\"", (int)type);
throw new MessageException("Unknown Mode Type");
}
}
}

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
{
@@ -25,18 +28,42 @@ namespace Netch.Utils
}
catch (Exception e)
{
Log.Error(e, "获取保留端口错误");
Log.Error(e, "Get reserved ports failed");
}
}
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)
@@ -59,7 +86,7 @@ namespace Netch.Utils
foreach (var line in output.SplitRemoveEmptyEntriesAndTrimEntries('\n'))
{
var value = line.Trim().SplitRemoveEmptyEntries(' ');
if (value.Length != 2)
if (value.Length < 2)
continue;
if (!ushort.TryParse(value[0], out var start) || !ushort.TryParse(value[1], out var end))
@@ -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;
}
}
@@ -166,8 +195,8 @@ namespace Netch.Utils
[Flags]
public enum PortType
{
TCP = 0x01,
UDP = 0x10,
TCP = 0b_01,
UDP = 0b_10,
Both = TCP | UDP
}

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

@@ -2,11 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Netch.Interfaces;
using Netch.Models;
using Timer = System.Timers.Timer;
namespace Netch.Utils
{
@@ -18,115 +14,24 @@ namespace Netch.Utils
.GetExportedTypes()
.Where(type => type.GetInterfaces().Contains(typeof(IServerUtil)));
ServerUtils = serversUtilsTypes.Select(t => (IServerUtil)Activator.CreateInstance(t)!).OrderBy(util => util.Priority);
ServerUtilDictionary = serversUtilsTypes.Select(t => (IServerUtil)Activator.CreateInstance(t)!).ToDictionary(util => util.TypeName);
}
public static Dictionary<string, IServerUtil> ServerUtilDictionary { get; }
public static IServerUtil GetUtilByTypeName(string typeName)
{
return ServerUtilDictionary.GetValueOrDefault(typeName) ?? throw new NotSupportedException("Specified server type is not supported.");
}
public static IServerUtil? GetUtilByUriScheme(string scheme)
{
return ServerUtilDictionary.Values.SingleOrDefault(i => i.UriScheme.Any(s => s.Equals(scheme)));
}
public static Type GetTypeByTypeName(string typeName)
{
return ServerUtils.Single(i => i.TypeName.Equals(typeName)).ServerType;
return GetUtilByTypeName(typeName).ServerType;
}
#region Delay
public static class DelayTestHelper
{
private static readonly Timer Timer;
private static readonly object TestAllLock = new();
public static readonly NumberRange Range = new(0, int.MaxValue / 1000);
static DelayTestHelper()
{
Timer = new Timer
{
Interval = 10000,
AutoReset = true
};
Timer.Elapsed += (_, _) => TestAllDelay();
}
public static bool Enabled
{
get => Timer.Enabled;
set
{
if (!ValueIsEnabled(Global.Settings.DetectionTick))
return;
Timer.Enabled = value;
}
}
public static int Interval => (int)(Timer.Interval / 1000);
private static bool ValueIsEnabled(int value)
{
return value != 0 && Range.InRange(value);
}
public static event EventHandler? TestDelayFinished;
public static void TestAllDelay()
{
if (!Monitor.TryEnter(TestAllLock))
return;
try
{
Parallel.ForEach(Global.Settings.Server, new ParallelOptions { MaxDegreeOfParallelism = 16 }, server => { server.Test(); });
TestDelayFinished?.Invoke(null, new EventArgs());
}
catch (Exception)
{
// ignored
}
finally
{
Monitor.Exit(TestAllLock);
}
}
public static void UpdateInterval()
{
Timer.Stop();
if (!ValueIsEnabled(Global.Settings.DetectionTick))
return;
Timer.Interval = Global.Settings.DetectionTick * 1000;
Task.Run(TestAllDelay);
Timer.Start();
}
}
#endregion
#region Handler
public static readonly IEnumerable<IServerUtil> ServerUtils;
public static IServerUtil GetUtilByTypeName(string typeName)
{
if (string.IsNullOrEmpty(typeName))
throw new ArgumentNullException();
return ServerUtils.Single(i => i.TypeName.Equals(typeName));
}
public static IServerUtil GetUtilByFullName(string fullName)
{
if (string.IsNullOrEmpty(fullName))
throw new ArgumentNullException();
return ServerUtils.Single(i => i.FullName.Equals(fullName));
}
public static IServerUtil? GetUtilByUriScheme(string typeName)
{
return ServerUtils.SingleOrDefault(i => i.UriScheme.Any(s => s.Equals(typeName)));
}
#endregion
}
}

View File

@@ -4,9 +4,9 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using Netch.JsonConverter;
using Netch.Models;
using Netch.Servers.Shadowsocks;
using Netch.Servers.Shadowsocks.Models;
using Netch.Servers;
using Serilog;
namespace Netch.Utils
@@ -33,7 +33,7 @@ namespace Netch.Utils
try
{
list.AddRange(JsonSerializer.Deserialize<List<ShadowsocksConfig>>(text)!.Select(server => new Shadowsocks
list.AddRange(JsonSerializer.Deserialize<List<ShadowsocksConfig>>(text)!.Select(server => new ShadowsocksServer
{
Hostname = server.server,
Port = server.server_port,
@@ -54,13 +54,13 @@ namespace Netch.Utils
}
catch (Exception e)
{
Log.Error(e, "从分享链接导入服务器异常");
Log.Error(e, "Parse servers from share link error");
}
}
}
catch (Exception e)
{
Log.Error(e, "从分享链接导入服务器异常");
Log.Error(e, "Parse servers from share link error");
}
return list;
@@ -85,7 +85,7 @@ namespace Netch.Utils
if (util != null)
list.AddRange(util.ParseUri(text));
else
Log.Warning("无法处理 {Scheme} 协议订阅链接", scheme);
Log.Warning("\"{Scheme}\" scheme share link not supported", scheme);
}
foreach (var node in list.Where(node => !node.Remark.IsNullOrWhiteSpace()))

View File

@@ -0,0 +1,110 @@
using System;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Netch.Models;
using Netch.Servers;
using Socks5.Models;
using STUN.Client;
using STUN.Enums;
using STUN.Proxy;
using STUN.StunResult;
namespace Netch.Utils
{
public static class Socks5ServerTestUtils
{
public static async Task<NatTypeTestResult> DiscoveryNatTypeAsync(Socks5Server socks5, CancellationToken ctx = default)
{
var stunServer = Global.Settings.STUN_Server;
var port = (ushort)Global.Settings.STUN_Server_Port;
var local = new IPEndPoint(IPAddress.Any, 0);
var socks5Option = new Socks5CreateOption
{
Address = await DnsUtils.LookupAsync(socks5.Hostname),
Port = socks5.Port,
UsernamePassword = new UsernamePassword
{
UserName = socks5.Username,
Password = socks5.Password
}
};
var ip = await DnsUtils.LookupAsync(stunServer);
if (ip == null)
{
return new NatTypeTestResult { Result = "Wrong STUN Server!" };
}
using IUdpProxy proxy = ProxyFactory.CreateProxy(ProxyType.Socks5, new IPEndPoint(IPAddress.Loopback, 0), socks5Option);
using var client = new StunClient5389UDP(new IPEndPoint(ip, port), local, proxy);
await client.ConnectProxyAsync(ctx);
try
{
await client.QueryAsync(ctx);
}
finally
{
await client.CloseProxyAsync(ctx);
}
var res = client.State;
var result = GetSimpleResult(res);
return new NatTypeTestResult
{
Result = result,
LocalEnd = res.LocalEndPoint?.ToString(),
PublicEnd = res.PublicEndPoint?.ToString()
};
}
private static string GetSimpleResult(StunResult5389 res)
{
switch (res.BindingTestResult, res.MappingBehavior, res.FilteringBehavior)
{
case (BindingTestResult.Fail, _, _):
return "NoUDP";
case (not BindingTestResult.Success, _, _):
return res.BindingTestResult.ToString();
case (_, MappingBehavior.Direct or MappingBehavior.EndpointIndependent, FilteringBehavior.EndpointIndependent):
return "1";
case (_, MappingBehavior.Direct or MappingBehavior.EndpointIndependent, _):
return "2";
case (_, MappingBehavior.AddressDependent or MappingBehavior.AddressAndPortDependent, _):
return "3";
case (_, MappingBehavior.Fail, _):
return MappingBehavior.Fail.ToString();
default:
return res.FilteringBehavior.ToString();
}
}
public static async Task<int?> HttpConnectAsync(Socks5Server socks5, CancellationToken ctx)
{
var socks5Option = new Socks5CreateOption
{
Address = await DnsUtils.LookupAsync(socks5.Hostname),
Port = socks5.Port,
UsernamePassword = new UsernamePassword
{
UserName = socks5.Username,
Password = socks5.Password
}
};
var stopwatch = Stopwatch.StartNew();
var result = await Socks5.Utils.Socks5TestUtils.Socks5ConnectAsync(socks5Option, token: ctx);
stopwatch.Stop();
if (result)
return Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
return null;
}
}
}

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,23 +1,23 @@
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
{
public static class Subscription
public static class SubscriptionUtil
{
private static readonly object ServerLock = new();
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.Subscription.Select(item => UpdateServerCoreAsync(item, proxyServer)));
}
public static void UpdateServer(SubscribeLink item, string? proxyServer)
private static async Task UpdateServerCoreAsync(Subscription 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;
@@ -53,8 +53,8 @@ namespace Netch.Utils
}
catch (Exception e)
{
Global.MainForm.NotifyTip($"{i18N.TranslateFormat("Update servers error from {0}", item.Remark)}\n{e.Message}", info: false);
Log.Warning(e, "更新服务器失败");
Global.MainForm.NotifyTip($"{i18N.TranslateFormat("Update servers failed from {0}", item.Remark)}\n{e.Message}", info: false);
Log.Warning(e, "Update servers failed");
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
@@ -13,14 +14,14 @@ namespace Netch.Utils
var mc = new ManagementClass("Win32_SystemDriver");
foreach (var obj in mc.GetInstances().Cast<ManagementObject>())
{
if (!(bool) obj["Started"])
if (!(bool)obj["Started"])
continue;
var path = obj["PathName"].ToString();
if (path == null)
continue;
var vendorExclude = new[] {"microsoft", "intel", "amd", "nvidia", "realtek"};
var vendorExclude = new[] { "microsoft", "intel", "amd", "nvidia", "realtek" };
var vendorName = FileVersionInfo.GetVersionInfo(path).LegalCopyright ?? string.Empty;
if (!allDriver && vendorExclude.Any(s => vendorName.Contains(s, StringComparison.OrdinalIgnoreCase)))
continue;
@@ -31,22 +32,28 @@ namespace Netch.Utils
public static IEnumerable<string> Processes(bool mask)
{
var hashset = new HashSet<string>();
var sortedSet = new SortedSet<string>();
var windowsFolder = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
var windowsAppsFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WindowsApps");
var userProfileFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
foreach (var process in Process.GetProcesses())
{
try
{
if (process.Id is 0 or 4)
var path = process.MainModule?.FileName;
if (path == null)
continue;
if (process.MainModule!.FileName!.StartsWith(Environment.GetFolderPath(Environment.SpecialFolder.Windows), StringComparison.OrdinalIgnoreCase))
if (path.StartsWith(windowsFolder, StringComparison.OrdinalIgnoreCase))
continue;
if (path.StartsWith(windowsAppsFolder, StringComparison.OrdinalIgnoreCase))
continue;
var path = process.MainModule.FileName;
if (mask)
path = path.Replace(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "%USERPROFILE%");
hashset.Add(path);
sortedSet.Add(path.Replace(userProfileFolder, "%USERPROFILE%"));
else
sortedSet.Add(path);
}
catch (Exception)
{
@@ -54,7 +61,7 @@ namespace Netch.Utils
}
}
return hashset;
return sortedSet;
}
}
}

Some files were not shown because too many files have changed in this diff Show More