Compare commits

..

59 Commits

Author SHA1 Message Date
ChsBuffer
947bf2b3ca Bump version to 1.8.3-Beta1 2021-03-21 22:53:00 +08:00
ChsBuffer
2a165c79df Start Profile, Refactor Save LastSelectedServer/Mode 2021-03-21 22:44:15 +08:00
ChsBuffer
55280df299 Update Load configuration 2021-03-21 22:00:16 +08:00
ChsBuffer
af48e7119e Auto reload modes, Lazy load mode rules 2021-03-21 04:54:33 +08:00
ChsBuffer
a1b978a22c Remove WindowsJobAPI 2021-03-21 03:38:34 +08:00
ChsBuffer
d08a9d5bfd Refactor Start Port Check Kill Process 2021-03-21 03:38:25 +08:00
ChsBuffer
c69c40750a Fix a typo 2021-03-21 01:48:44 +08:00
ChsBuffer
77376502b7 Update CI 2021-03-21 00:05:20 +08:00
ChsBuffer
fdfc3f11eb Update NFController 2021-03-20 21:38:24 +08:00
AmazingDM
0d956efac6 update binaries 2021-03-20 20:36:37 +08:00
AmazingDM
3f9709167d update NFController
optimization rdr
2021-03-20 20:35:56 +08:00
ChsBuffer
425e468f78 Revert "Update Netch.csproj to be compatible with dotnet cli"
This reverts commit 77f2b761fc.
2021-03-20 18:33:04 +08:00
AmazingDM
a485a4647c fix error set TYPE_FILTERCHILDPROC 2021-03-20 17:56:45 +08:00
ChsBuffer
0b484face4 Update Edit Process Mode Form 2021-03-20 04:33:35 +08:00
ChsBuffer
e0b5b0e49c Restore Edit Process Scan Button And Update Select Button 2021-03-20 03:41:37 +08:00
ChsBuffer
f519850ffc Trim 2021-03-20 00:50:06 +08:00
ChsBuffer
4513a68e73 Update Nuget packages
Bump MSTest.TestAdapter from 2.2.1 to 2.2.3
Bump MSTest.TestFramework from 2.2.1 to 2.2.3
2021-03-19 03:58:57 +08:00
ChsBuffer
95de42e778 Update SS/SSR Parameter 2021-03-19 03:56:54 +08:00
ChsBuffer
18168c3a4e Add Nullable.Extended.Analyzer 2021-03-19 03:50:08 +08:00
ChsBuffer
77f2b761fc Update Netch.csproj to be compatible with dotnet cli 2021-03-19 03:07:07 +08:00
ChsBuffer
9bd02ec122 Update Debug Logging 2021-03-19 03:05:57 +08:00
ChsBuffer
cfb4a5b3f6 The Debug configuration will make the build attach to the console and write the application log to standard output 2021-03-19 00:58:34 +08:00
ChsBuffer
6178045f15 Fix Import v2rayN format sharelink got exception when property type Number 2021-03-16 16:43:46 +08:00
ChsBuffer
4773de99e5 Fix typo 2021-03-16 16:41:35 +08:00
ChsBuffer
eb713db867 Refactor: generate SS/SSR start arguments 2021-03-16 15:55:46 +08:00
ChsBuffer
15f4895c0f Update UnitTest 2021-03-16 15:24:30 +08:00
ChsBuffer
afbda60dfb format NFController 2021-03-16 15:19:25 +08:00
ChsBuffer
f51229f2c8 Fix: SS/SSR password not allow empty and Update Model 2021-03-16 15:19:12 +08:00
ChsBuffer
26f9ae3958 Update Server Model value type 2021-03-15 22:38:33 +08:00
ChsBuffer
dfc680f0b7 Update .gitignore 2021-03-15 22:10:46 +08:00
ChsBuffer
5e56556534 Remove unused resources 2021-03-15 22:03:47 +08:00
ChsBuffer
33a0d9e7c2 Bump version to 1.8.2 2021-03-14 18:09:17 +08:00
ChsBuffer
939d600be1 Refactor SettingForm 2021-03-14 17:48:38 +08:00
ChsBuffer
a1e511915e Refactor: MainForm Start failed MessageBoxX level to Error 2021-03-13 02:32:04 +08:00
ChsBuffer
94796110d5 Refactor Mode Form
Process Mode Form Scan to Select
Remove UseCustomName
Mode.get_FullRule throw MessageException
2021-03-13 02:27:38 +08:00
ChsBuffer
5c3e2ab207 Refactor Updater 2021-03-13 01:13:54 +08:00
ChsBuffer
e66eb9759a Refactor LoadModes 2021-03-13 01:13:39 +08:00
ChsBuffer
b27ccfab17 Add more comments to Updater
Updater Keeps disabled file
LazyLoad Global.Mutex
Fix AfterUpdate Release Global.Mutex
2021-03-13 00:26:21 +08:00
ChsBuffer
87b3867095 Feature: Load mode ignores directories with "disabled" files in the directory 2021-03-12 23:41:36 +08:00
ChsBuffer
3d1538264a Update docs 2021-03-12 15:46:39 +08:00
AmazingDM
0bffbbfb62 Merge pull request #564 from NetchX/dependabot/submodules/modes-42375ef
Bump modes from `7bcbf86` to `42375ef`
2021-03-12 11:10:21 +08:00
dependabot[bot]
6ccbcae31f Bump modes from 7bcbf86 to 42375ef
Bumps [modes](https://github.com/NetchX/NetchMode) from `7bcbf86` to `42375ef`.
- [Release notes](https://github.com/NetchX/NetchMode/releases)
- [Commits](7bcbf86f5c...42375ef724)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-11 23:05:46 +00:00
ChsBuffer
5a9d6e145d Cut SS DLL 2021-03-11 13:12:26 +08:00
ChsBuffer
1b7eb6f6de Merge pull request #561 from NetchX/dependabot/submodules/modes-7bcbf86
Bump modes from `dd436b3` to `7bcbf86`
2021-03-11 13:07:57 +08:00
dependabot[bot]
62bfa25870 Bump modes from dd436b3 to 7bcbf86
Bumps [modes](https://github.com/NetchX/NetchMode) from `dd436b3` to `7bcbf86`.
- [Release notes](https://github.com/NetchX/NetchMode/releases)
- [Commits](dd436b3652...7bcbf86f5c)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-11 04:29:53 +00:00
ChsBuffer
a1d481ba05 Update dependabot check submodule update interval 2021-03-11 12:21:35 +08:00
ChsBuffer
39abcb7d79 Merge pull request #555 from NetchX/dependabot/nuget/WindowsProxy-5.0.3
Bump WindowsProxy from 5.0.0 to 5.0.3
2021-03-09 07:09:05 +08:00
dependabot[bot]
7c1df3786e Bump WindowsProxy from 5.0.0 to 5.0.3
Bumps [WindowsProxy](https://github.com/HMBSbige/WindowsProxy) from 5.0.0 to 5.0.3.
- [Release notes](https://github.com/HMBSbige/WindowsProxy/releases)
- [Commits](https://github.com/HMBSbige/WindowsProxy/compare/5.0.0...5.0.3)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-08 23:05:14 +00:00
AmazingDM
fe11ee56ba update NF driver to 1.6.0.7
update RDR ChildProcessHandle
2021-03-08 12:06:24 +08:00
ChsBuffer
4f38de4ee9 remove HTTPController.Stop try catch set state 2021-03-08 10:55:23 +08:00
ChsBuffer
1aa32eaf3a HTTPController.Stop check state validity 2021-03-08 09:07:50 +08:00
AmazingDM
4b3d6fb3bf DNS , ICMP Redirector
Child process handle
2021-03-06 14:04:50 +08:00
ChsBuffer
12559d8192 Fix MainController.Stop() didn't catch multiple exceptions 2021-03-06 05:05:21 +08:00
ChsBuffer
bd71452206 MainController.StopAsync() Exception handling 2021-03-06 04:49:11 +08:00
ChsBuffer
10ba299f4d Refactor Updater exception and release UpdaterChecker Event's EventHandler
Fix didn't Remove broken update file
2021-03-06 02:56:41 +08:00
ChsBuffer
1ff9d1ec9d Maybe fix #545 2021-03-05 22:13:27 +08:00
ChsBuffer
9cbb88c886 Open Opened Netch Window using win32 apis 2021-03-05 21:19:10 +08:00
ChsBuffer
7e65ae0b6b Open Opened Netch Window using win32 apis 2021-03-05 18:01:30 +08:00
ChsBuffer
41491f8c20 Cut OnlyInstance 2021-03-05 16:07:21 +08:00
79 changed files with 1666 additions and 1562 deletions

View File

@@ -26,8 +26,7 @@ updates:
- package-ecosystem: "gitsubmodule"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
interval: "daily"
time: "07:15"
timezone: "Asia/Shanghai"
labels:

30
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Netch CI
on:
push:
branches-ignore:
dependabot/**
pull_request:
jobs:
build:
name: Build
runs-on: windows-latest
steps:
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1.0.2
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
- name: Build Solution
shell: pwsh
run: .\BUILD.ps1
- name: Upload Artifact
continue-on-error: true
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
uses: actions/upload-artifact@v2
with:
name: Netch
path: Netch\bin\x64\Release

View File

@@ -1,5 +1,8 @@
name: Netch CI
on: [push, pull_request]
name: Netch Release
on:
push:
tags:
- '*.*'
jobs:
build:
name: Build
@@ -17,14 +20,6 @@ jobs:
shell: pwsh
run: .\BUILD.ps1
- name: Upload Artifact
continue-on-error: true
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
uses: actions/upload-artifact@v2
with:
name: Netch
path: Netch\bin\x64\Release
- name: Package
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
shell: pwsh
@@ -39,10 +34,8 @@ jobs:
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
with:
name: ${{ env.GITHUB_TAG_NAME }}
prerelease: true
prerelease: ${{ contains(github.ref, '-') }}
draft: false
files: |
C:\builtfiles\Netch.7z
@@ -55,4 +48,4 @@ jobs:
## 校验和
| 文件名 | SHA256 |
| :- | :- |
| Netch.7z | ${{ env.Netch_SHA256 }} |
| Netch.7z | ${{ env.Netch_SHA256 }} |

7
.gitignore vendored
View File

@@ -1,4 +1,5 @@
/.vs
/packages
.vs/
.idea/
/*.user
*/bin/
*/obj/
*.csproj.user

3
Netch/.gitignore vendored
View File

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

View File

@@ -24,7 +24,6 @@ namespace Netch.Controllers
public void Start(in Mode mode)
{
PrivoxyController.Start(MainController.Server!);
Global.Job.AddProcess(PrivoxyController.Instance!);
string? pacUrl = null;
if (MainController.Server is Socks5 or Trojan && mode.BypassChina || (Global.Settings.AlwaysStartPACServer ?? false))
@@ -38,7 +37,7 @@ namespace Netch.Controllers
Global.Settings.Pac_Port = PortHelper.GetAvailablePort();
}
pacUrl = PACServerHandle.InitPACServer("127.0.0.1");
pacUrl = PACServerHandle.InitPACServer();
}
if (mode.Type is 3)
@@ -76,7 +75,14 @@ namespace Netch.Controllers
if (_oldState != null)
{
using var service = new ProxyService();
service.Set(_oldState!);
if (_oldState.IsProxy && _oldState.ProxyServer == service.Query().ProxyServer ||
_oldState.IsAutoProxyUrl && _oldState.AutoConfigUrl!.StartsWith(PACServerHandle.PacPrefix))
{
service.Direct();
return;
}
service.Set(_oldState);
}
})
};

View File

@@ -1,11 +1,10 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Netch.Models;
using Netch.Servers.Socks5;
using Netch.Utils;
using static Netch.Utils.PortHelper;
namespace Netch.Controllers
{
@@ -49,7 +48,12 @@ namespace Netch.Controllers
/// <param name="mode">模式</param>
/// <returns>是否启动成功</returns>
/// <exception cref="MessageException"></exception>
public static async Task Start(Server server, Mode mode)
public static async Task StartAsync(Server server, Mode mode)
{
await Task.Run(() => Start(server, mode));
}
public static void Start(Server server, Mode mode)
{
Logging.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}");
Server = server;
@@ -71,23 +75,15 @@ namespace Netch.Controllers
{
if (!ModeHelper.SkipServerController(server, mode))
{
await Task.Run(() => StartServer(server, mode, out _serverController));
StartServer(server, mode, out _serverController);
StatusPortInfoText.UpdateShareLan();
}
await Task.Run(() => StartMode(mode));
StartMode(mode);
}
catch (Exception e)
{
try
{
await Stop();
}
catch
{
// ignored
}
Stop();
switch (e)
{
@@ -108,20 +104,11 @@ namespace Netch.Controllers
{
controller = ServerHelper.GetUtilByTypeName(server.Type).GetController();
if (controller is Guard instanceController)
Utils.Utils.KillProcessByName(instanceController.MainFile);
PortCheck(controller.Socks5LocalPort(), "Socks5");
TryReleaseTcpPort(controller.Socks5LocalPort(), "Socks5");
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", controller.Name));
controller.Start(in server, mode);
if (controller is Guard {Instance: { }} guard)
Task.Run(() =>
{
Thread.Sleep(1000);
Global.Job.AddProcess(guard.Instance!);
});
if (server is Socks5 socks5)
{
@@ -136,25 +123,28 @@ namespace Netch.Controllers
private static void StartMode(Mode mode)
{
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName, out var portType);
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName);
if (ModeController == null)
return;
if (port != null)
PortCheck((ushort) port, portName, portType);
TryReleaseTcpPort((ushort) port, portName);
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
ModeController.Start(mode);
if (ModeController is Guard {Instance: { }} guard)
Global.Job.AddProcess(guard.Instance!);
}
public static async Task StopAsync()
{
await Task.Run(Stop);
}
/// <summary>
/// 停止
/// </summary>
public static async Task Stop()
public static void Stop()
{
if (_serverController == null && ModeController == null)
return;
@@ -169,7 +159,16 @@ namespace Netch.Controllers
Task.Run(() => ModeController?.Stop())
};
await Task.WhenAll(tasks);
try
{
Task.WaitAll(tasks);
}
catch (Exception e)
{
Logging.Error(e.ToString());
Utils.Utils.Open(Logging.LogFile);
}
ModeController = null;
ServerController = null;
}
@@ -178,7 +177,7 @@ namespace Netch.Controllers
{
try
{
CheckPort(port, portType);
PortHelper.CheckPort(port, portType);
}
catch (PortInUseException)
{
@@ -189,6 +188,27 @@ namespace Netch.Controllers
throw new MessageException(i18N.TranslateFormat("The {0} port is reserved by system.", $"{portName} ({port})"));
}
}
public static void TryReleaseTcpPort(ushort port, string portName)
{
Process? p;
if ((p = PortHelper.GetProcessByUsedTcpPort(port)) != null)
{
if (p.MainModule!.FileName.StartsWith(Global.NetchDir))
{
p.Kill();
p.WaitForExit();
}
else
{
throw new MessageException(i18N.TranslateFormat("The {0} port is used by {1}.",
$"{portName} ({port})",
$"({p.Id}){p.MainModule.FileName}"));
}
}
PortCheck(port, portName, PortType.TCP);
}
}
public class MessageException : Exception

View File

@@ -17,67 +17,30 @@ namespace Netch.Controllers
{
private static readonly ServiceController NFService = new("netfilter2");
private static readonly string BinDriver;
private const string BinDriver = "bin\\nfdriver.sys";
private static readonly string SystemDriver = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
private static string? _sysDns;
private OutboundAdapter? _outbound;
static NFController()
{
string fileName;
switch ($"{Environment.OSVersion.Version.Major}.{Environment.OSVersion.Version.Minor}")
{
case "10.0":
fileName = "Win-10.sys";
break;
case "6.3":
case "6.2":
fileName = "Win-8.sys";
break;
case "6.1":
case "6.0":
fileName = "Win-7.sys";
break;
default:
throw new MessageException($"不支持的系统版本:{Environment.OSVersion.Version}");
}
BinDriver = "bin\\" + fileName;
}
public string Name { get; } = "Redirector";
public void Start(in Mode mode)
{
CheckDriver();
#region aio_dial
aio_dial((int) NameList.TYPE_FILTERLOOPBACK, "false");
aio_dial((int) NameList.TYPE_TCPLISN, Global.Settings.RedirectorTCPPort.ToString());
// Server
aio_dial((int) NameList.TYPE_FILTERUDP, (Global.Settings.ProcessProxyProtocol != PortType.TCP).ToString().ToLower());
aio_dial((int) NameList.TYPE_FILTERTCP, (Global.Settings.ProcessProxyProtocol != PortType.UDP).ToString().ToLower());
SetServer(Global.Settings.ProcessProxyProtocol);
dial_Server(Global.Settings.ProcessProxyProtocol);
if (!CheckRule(mode.FullRule, out var list))
throw new MessageException($"\"{string.Join("", list.Select(s => s + "\n"))}\" does not conform to C++ regular expression syntax");
// Mode Rule
dial_Name(mode);
SetName(mode);
#endregion
if (Global.Settings.ModifySystemDNS)
{
_outbound = new OutboundAdapter();
// 备份并替换系统 DNS
_sysDns = _outbound.DNS;
if (string.IsNullOrWhiteSpace(Global.Settings.ModifiedDNS))
Global.Settings.ModifiedDNS = "1.1.1.1,8.8.8.8";
_outbound.DNS = Global.Settings.ModifiedDNS;
}
// Features
aio_dial((int) NameList.TYPE_REDIRCTOR_DNS, Global.Settings.RedirectDNS ? Global.Settings.RedirectDNSAddr : "");
aio_dial((int) NameList.TYPE_REDIRCTOR_ICMP, Global.Settings.RedirectICMP ? Global.Settings.RedirectICMPAddr : "");
aio_dial((int) NameList.TYPE_FILTERCHILDPROC, Global.Settings.ChildProcessHandle.ToString().ToLower());
if (!aio_init())
throw new MessageException("Redirector Start failed, run Netch with \"-console\" argument");
@@ -85,34 +48,17 @@ namespace Netch.Controllers
public void Stop()
{
Task.Run(() =>
{
if (Global.Settings.ModifySystemDNS)
//恢复系统DNS
_outbound!.DNS = _sysDns!;
});
aio_free();
}
/// <summary>
/// </summary>
/// <param name="rules"></param>
/// <param name="incompatibleRule"></param>
/// <returns>No Problem true</returns>
public static bool CheckRule(IEnumerable<string> rules, out IEnumerable<string> incompatibleRule)
{
incompatibleRule = rules.Where(r => !CheckCppRegex(r, false));
aio_dial((int) NameList.TYPE_CLRNAME, "");
return !incompatibleRule.Any();
}
#region CheckRule
/// <summary>
/// </summary>
/// <param name="r"></param>
/// <param name="clear"></param>
/// <returns>No Problem true</returns>
public static bool CheckCppRegex(string r, bool clear = true)
private static bool CheckCppRegex(string r, bool clear = true)
{
try
{
@@ -128,50 +74,31 @@ namespace Netch.Controllers
}
}
private static void CheckDriver()
/// <summary>
/// </summary>
/// <param name="rules"></param>
/// <param name="results"></param>
/// <returns>No Problem true</returns>
public static bool CheckRules(IEnumerable<string> rules, out IEnumerable<string> results)
{
var binFileVersion = Utils.Utils.GetFileVersion(BinDriver);
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
Logging.Info("内置驱动版本: " + binFileVersion);
Logging.Info("系统驱动版本: " + systemFileVersion);
if (!File.Exists(SystemDriver))
{
InstallDriver();
return;
}
var reinstallFlag = false;
if (Version.TryParse(binFileVersion, out var binResult) && Version.TryParse(systemFileVersion, out var systemResult))
{
if (binResult.CompareTo(systemResult) > 0)
// Bin greater than Installed
reinstallFlag = true;
else if (systemResult.Major != binResult.Major)
// Installed greater than Bin but Major Version Difference (has breaking changes), do downgrade
reinstallFlag = true;
}
else
{
if (!systemFileVersion.Equals(binFileVersion))
reinstallFlag = true;
}
if (!reinstallFlag)
return;
Logging.Info("更新驱动");
UninstallDriver();
InstallDriver();
results = rules.Where(r => !CheckCppRegex(r, false));
aio_dial((int) NameList.TYPE_CLRNAME, "");
return !results.Any();
}
private void SetServer(in PortType portType)
public static string GenerateInvalidRulesMessage(IEnumerable<string> rules)
{
return $"{string.Join("\n", rules)}\nAbove rules does not conform to C++ regular expression syntax";
}
#endregion
private void dial_Server(in PortType portType)
{
if (portType == PortType.Both)
{
SetServer(PortType.TCP);
SetServer(PortType.UDP);
dial_Server(PortType.TCP);
dial_Server(PortType.UDP);
return;
}
@@ -204,8 +131,8 @@ namespace Netch.Controllers
{
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Shadowsocks");
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"{shadowsocks.AutoResolveHostname()}:{shadowsocks.Port}");
aio_dial((int) NameList.TYPE_TCPMETH + offset, shadowsocks.EncryptMethod ?? string.Empty);
aio_dial((int) NameList.TYPE_TCPPASS + offset, shadowsocks.Password ?? string.Empty);
aio_dial((int) NameList.TYPE_TCPMETH + offset, shadowsocks.EncryptMethod);
aio_dial((int) NameList.TYPE_TCPPASS + offset, shadowsocks.Password);
}
else
{
@@ -217,68 +144,74 @@ namespace Netch.Controllers
}
}
private void SetName(Mode mode)
private void dial_Name(Mode mode)
{
aio_dial((int) NameList.TYPE_CLRNAME, "");
foreach (var rule in mode.FullRule)
var list = new List<string>();
foreach (var s in mode.FullRule)
{
if (rule.StartsWith("!"))
if (s.StartsWith("!"))
{
aio_dial((int) NameList.TYPE_BYPNAME, rule.Substring(1));
if (!aio_dial((int) NameList.TYPE_BYPNAME, s.Substring(1)))
list.Add(s);
continue;
}
aio_dial((int) NameList.TYPE_ADDNAME, rule);
if (!aio_dial((int) NameList.TYPE_ADDNAME, s))
list.Add(s);
}
if (list.Any())
throw new MessageException(GenerateInvalidRulesMessage(list));
aio_dial((int) NameList.TYPE_ADDNAME, @"NTT\.exe");
aio_dial((int) NameList.TYPE_BYPNAME, "^" + Global.NetchDir.ToRegexString() + @"((?!NTT\.exe).)*$");
}
#region NativeMethods
#region DriverUtil
private const int UdpNameListOffset = (int) NameList.TYPE_UDPTYPE - (int) NameList.TYPE_TCPTYPE;
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
private static extern bool aio_dial(int name, [MarshalAs(UnmanagedType.LPWStr)] string value);
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
private static extern bool aio_init();
[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();
public enum NameList
private static void CheckDriver()
{
TYPE_FILTERLOOPBACK,
TYPE_FILTERTCP,
TYPE_FILTERUDP,
TYPE_TCPLISN,
TYPE_TCPTYPE,
TYPE_TCPHOST,
TYPE_TCPUSER,
TYPE_TCPPASS,
TYPE_TCPMETH,
TYPE_UDPTYPE,
TYPE_UDPHOST,
TYPE_UDPUSER,
TYPE_UDPPASS,
TYPE_UDPMETH,
TYPE_ADDNAME,
TYPE_BYPNAME,
TYPE_CLRNAME
var binFileVersion = Utils.Utils.GetFileVersion(BinDriver);
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
Logging.Info("内置驱动版本: " + binFileVersion);
Logging.Info("系统驱动版本: " + systemFileVersion);
if (!File.Exists(SystemDriver))
{
// Install
InstallDriver();
return;
}
var reinstall = false;
if (Version.TryParse(binFileVersion, out var binResult) && Version.TryParse(systemFileVersion, out var systemResult))
{
if (binResult.CompareTo(systemResult) > 0)
// Update
reinstall = true;
else if (systemResult.Major != binResult.Major)
// Downgrade when Major version different (may have breaking changes)
reinstall = true;
}
else
{
// Parse File versionName to Version failed
if (!systemFileVersion.Equals(binFileVersion))
// versionNames are different, Reinstall
reinstall = true;
}
if (!reinstall)
return;
Logging.Info("更新驱动");
UninstallDriver();
InstallDriver();
}
#endregion
#region Utils
/// <summary>
/// 安装 NF 驱动
/// </summary>
@@ -344,5 +277,61 @@ namespace Netch.Controllers
}
#endregion
#region NativeMethods
private const int UdpNameListOffset = (int) NameList.TYPE_UDPTYPE - (int) NameList.TYPE_TCPTYPE;
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
private static extern bool aio_dial(int name, [MarshalAs(UnmanagedType.LPWStr)] string value);
[DllImport("Redirector.bin", CallingConvention = CallingConvention.Cdecl)]
private static extern bool aio_init();
[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();
public enum NameList
{
//bool
TYPE_FILTERLOOPBACK,
TYPE_FILTERTCP,
TYPE_FILTERUDP,
TYPE_FILTERIP,
TYPE_FILTERCHILDPROC, //子进程捕获
TYPE_TCPLISN,
TYPE_TCPTYPE,
TYPE_TCPHOST,
TYPE_TCPUSER,
TYPE_TCPPASS,
TYPE_TCPMETH,
TYPE_UDPTYPE,
TYPE_UDPHOST,
TYPE_UDPUSER,
TYPE_UDPPASS,
TYPE_UDPMETH,
TYPE_ADDNAME,
TYPE_ADDFIP,
TYPE_BYPNAME,
TYPE_CLRNAME,
TYPE_CLRFIP,
//str addr x.x.x.x only ipv4
TYPE_REDIRCTOR_DNS,
TYPE_REDIRCTOR_ICMP
}
#endregion
}
}

View File

@@ -47,7 +47,7 @@ namespace Netch.Controllers
Global.MainForm.BeginInvoke(new Action(() =>
{
if (!_form!.IsDisposed)
_form!.richTextBox1.AppendText(line + "\n");
_form.richTextBox1.AppendText(line + "\n");
}));
}

View File

@@ -18,8 +18,8 @@ namespace Netch.Controllers
public const string Name = @"Netch";
public const string Copyright = @"Copyright © 2019 - 2021";
public const string AssemblyVersion = @"1.8.1";
private const string Suffix = @"";
public const string AssemblyVersion = @"1.8.3";
private const string Suffix = @"Beta1";
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
@@ -69,31 +69,25 @@ namespace Netch.Controllers
}
}
public static bool GetFileNameAndHashFromMarkdownForm(in string text, out string fileName, out string sha256, string? keyword = null)
public static void GetLatestUpdateFileNameAndHash(out string fileName, out string sha256, string? keyword = null)
{
IEnumerable<Match> matches;
try
{
matches = Regex.Matches(text, @"^\| (?<filename>.*) \| (?<sha256>.*) \|\r?$", RegexOptions.Multiline).Cast<Match>().Skip(2);
}
catch (Exception e)
{
Logging.Error(e.ToString());
throw new Exception(i18N.Translate("Find update filename and hash failed"));
}
fileName = string.Empty;
sha256 = string.Empty;
var matches = Regex.Matches(LatestRelease.body, @"^\| (?<filename>.*) \| (?<sha256>.*) \|\r?$", RegexOptions.Multiline)
.Cast<Match>()
.Skip(2);
/*
Skip(2)
| 文件名 | SHA256 |
| :- | :- |
*/
Match match = keyword == null ? matches.First() : matches.First(m => m.Groups["filename"].Value.Contains(keyword));
if (match != null)
{
fileName = match.Groups["filename"].Value;
sha256 = match.Groups["sha256"].Value;
return true;
}
fileName = string.Empty;
sha256 = string.Empty;
return false;
fileName = match.Groups["filename"].Value;
sha256 = match.Groups["sha256"].Value;
}
}
}

View File

@@ -423,7 +423,7 @@
this.ModeComboBox.Size = new System.Drawing.Size(546, 24);
this.ModeComboBox.TabIndex = 2;
this.ModeComboBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.ComboBox_DrawItem);
this.ModeComboBox.SelectedIndexChanged += new System.EventHandler(this.ModeComboBox_SelectedIndexChanged);
this.ModeComboBox.SelectionChangeCommitted += new System.EventHandler(this.ModeComboBox_SelectionChangeCommitted);
//
// ServerComboBox
//
@@ -438,7 +438,7 @@
this.ServerComboBox.Size = new System.Drawing.Size(546, 24);
this.ServerComboBox.TabIndex = 1;
this.ServerComboBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.ComboBox_DrawItem);
this.ServerComboBox.SelectedIndexChanged += new System.EventHandler(this.ServerComboBox_SelectedIndexChanged);
this.ServerComboBox.SelectionChangeCommitted += new System.EventHandler(this.ServerComboBox_SelectionChangeCommitted);
//
// tableLayoutPanel2
//

View File

@@ -30,7 +30,6 @@ namespace Netch.Forms
private readonly Dictionary<string, object> _mainFormText = new();
private bool _comboBoxInitialized;
private bool _textRecorded;
public MainForm()
@@ -85,7 +84,7 @@ namespace Netch.Forms
private void MainForm_Load(object sender, EventArgs e)
{
OnlyInstance.Called += OnCalled;
Netch.TimePoint("MainForm ctor (Pre MainForm Load)");
// 计算 ComboBox绘制 目标宽度
RecordSize();
@@ -95,7 +94,6 @@ namespace Netch.Forms
ModeHelper.Load();
LoadModes();
_comboBoxInitialized = true;
// 加载翻译
TranslateControls();
@@ -106,10 +104,6 @@ namespace Netch.Forms
// 加载快速配置
LoadProfiles();
// 打开软件时启动加速,产生开始按钮点击事件
if (Global.Settings.StartWhenOpened)
ControlButton_Click(null, null);
Task.Run(() =>
{
// 检查更新
@@ -117,12 +111,18 @@ namespace Netch.Forms
CheckUpdate();
});
Task.Run(async () =>
Task.Run(() =>
{
// 检查订阅更新
if (Global.Settings.UpdateServersWhenOpened)
await UpdateServersFromSubscribe(Global.Settings.UseProxyToUpdateSubscription);
UpdateServersFromSubscribe(Global.Settings.UseProxyToUpdateSubscription).Wait();
// 打开软件时启动加速,产生开始按钮点击事件
if (Global.Settings.StartWhenOpened)
ControlButton_Click(null, null);
});
Netch.TimePoint("Post Form Load", false);
}
private void RecordSize()
@@ -345,7 +345,7 @@ namespace Netch.Forms
Type = 5
};
await MainController.Start(server!, mode);
await MainController.StartAsync(server!, mode);
proxyServer = $"http://127.0.0.1:{Global.Settings.HTTPLocalPort}";
}
@@ -363,14 +363,7 @@ namespace Netch.Forms
finally
{
if (useProxy)
try
{
await MainController.Stop();
}
catch
{
// ignored
}
await MainController.StopAsync();
DisableItems(true);
}
@@ -386,19 +379,25 @@ namespace Netch.Forms
{
void OnNewVersionNotFound(object o, EventArgs args)
{
UpdateChecker.NewVersionNotFound -= OnNewVersionNotFound;
NotifyTip(i18N.Translate("Already latest version"));
}
void OnNewVersionFoundFailed(object o, EventArgs args)
{
UpdateChecker.NewVersionFoundFailed -= OnNewVersionFoundFailed;
NotifyTip(i18N.Translate("New version found failed"), info: false);
}
UpdateChecker.NewVersionNotFound += OnNewVersionNotFound;
UpdateChecker.NewVersionFoundFailed += OnNewVersionFoundFailed;
CheckUpdate();
try
{
UpdateChecker.NewVersionNotFound += OnNewVersionNotFound;
UpdateChecker.NewVersionFoundFailed += OnNewVersionFoundFailed;
CheckUpdate();
}
finally
{
UpdateChecker.NewVersionNotFound -= OnNewVersionNotFound;
UpdateChecker.NewVersionFoundFailed -= OnNewVersionFoundFailed;
}
});
}
@@ -460,7 +459,7 @@ namespace Netch.Forms
Type = 5
};
await MainController.Start(server, mode);
await MainController.StartAsync(server, mode);
}
}
@@ -479,7 +478,7 @@ namespace Netch.Forms
finally
{
if (useProxy)
await MainController.Stop();
await MainController.StopAsync();
StatusText();
Enabled = true;
@@ -572,6 +571,47 @@ namespace Netch.Forms
Utils.Utils.Open($"https://github.com/{UpdateChecker.Owner}/{UpdateChecker.Repo}/releases");
}
private async void NewVersionLabel_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Control || !UpdateChecker.LatestRelease!.assets.Any())
{
Utils.Utils.Open(UpdateChecker.LatestVersionUrl!);
return;
}
if (MessageBoxX.Show(i18N.Translate("Download and install now?"), confirm: true) != DialogResult.OK)
return;
NotifyTip(i18N.Translate("Start downloading new version"));
NewVersionLabel.Enabled = false;
NewVersionLabel.Text = "...";
try
{
await Task.Run(() =>
{
Updater.Updater.DownloadAndUpdate(Path.Combine(Global.NetchDir, "data"),
Global.NetchDir,
(_, args) => BeginInvoke(new Action(() => NewVersionLabel.Text = $"{args.ProgressPercentage}%")));
});
}
catch (Exception exception)
{
if (exception is not MessageException)
{
Logging.Error($"更新失败: {exception}");
Utils.Utils.Open(Logging.LogFile);
}
NotifyTip(exception.Message, info: false);
}
finally
{
NewVersionLabel.Visible = false;
NewVersionLabel.Enabled = true;
}
}
private void AboutToolStripButton_Click(object sender, EventArgs e)
{
Hide();
@@ -594,7 +634,7 @@ namespace Netch.Forms
{
// 停止
State = State.Stopping;
await MainController.Stop();
await MainController.StopAsync();
State = State.Stopped;
return;
}
@@ -621,13 +661,13 @@ namespace Netch.Forms
try
{
await MainController.Start(server, mode);
await MainController.StartAsync(server, mode);
}
catch (Exception exception)
{
State = State.Stopped;
StatusText(i18N.Translate("Start failed"));
MessageBoxX.Show(exception.Message);
MessageBoxX.Show(exception.Message, LogLevel.ERROR);
return;
}
@@ -697,13 +737,9 @@ namespace Netch.Forms
private void LoadServers()
{
var comboBoxInitialized = _comboBoxInitialized;
_comboBoxInitialized = false;
ServerComboBox.Items.Clear();
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
SelectLastServer();
_comboBoxInitialized = comboBoxInitialized;
}
public void SelectLastServer()
@@ -718,11 +754,8 @@ namespace Netch.Forms
// 如果当前 ServerComboBox 中没元素,不做处理
}
private void ServerComboBox_SelectedIndexChanged(object sender, EventArgs o)
private void ServerComboBox_SelectionChangeCommitted(object sender, EventArgs o)
{
if (!_comboBoxInitialized)
return;
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
}
@@ -821,14 +854,10 @@ namespace Netch.Forms
public void LoadModes()
{
var comboBoxInitialized = _comboBoxInitialized;
_comboBoxInitialized = false;
ModeComboBox.Items.Clear();
ModeComboBox.Items.AddRange(Global.Modes.ToArray());
ModeComboBox.Items.AddRange(Global.Modes.Cast<object>().ToArray());
ModeComboBox.Tag = null;
SelectLastMode();
_comboBoxInitialized = comboBoxInitialized;
}
public void SelectLastMode()
@@ -843,11 +872,8 @@ namespace Netch.Forms
// 如果当前 ModeComboBox 中没元素,不做处理
}
private void ModeComboBox_SelectedIndexChanged(object sender, EventArgs o)
private void ModeComboBox_SelectionChangeCommitted(object sender, EventArgs o)
{
if (!_comboBoxInitialized)
return;
try
{
Global.Settings.ModeComboBoxSelectedIndex = Global.Modes.IndexOf((Models.Mode) ModeComboBox.SelectedItem);
@@ -1362,27 +1388,12 @@ namespace Netch.Forms
File.Delete(file);
if (!IsWaiting())
await MainController.Stop();
await MainController.StopAsync();
Dispose();
Environment.Exit(Environment.ExitCode);
}
private void OnCalled(object sender, OnlyInstance.Commands e)
{
switch (e)
{
case OnlyInstance.Commands.Show:
NotifyIcon_MouseDoubleClick(null, null);
break;
case OnlyInstance.Commands.Exit:
Exit(true);
break;
default:
throw new ArgumentOutOfRangeException(nameof(e), e, null);
}
}
#region FormClosingButton
private bool _isFirstCloseWindow = true;
@@ -1409,43 +1420,22 @@ namespace Netch.Forms
private void CheckUpdate()
{
UpdateChecker.NewVersionFound += (_, _) =>
{
NotifyTip($"{i18N.Translate(@"New version available", ": ")}{UpdateChecker.LatestVersionNumber}");
NewVersionLabel.Visible = true;
};
UpdateChecker.Check(Global.Settings.CheckBetaUpdate).Wait();
}
private async void NewVersionLabel_Click(object sender, EventArgs e)
{
if (ModifierKeys == Keys.Control || !UpdateChecker.LatestRelease!.assets.Any())
{
Utils.Utils.Open(UpdateChecker.LatestVersionUrl!);
return;
}
if (MessageBoxX.Show(i18N.Translate("Download and install now?"), confirm: true) != DialogResult.OK)
return;
NotifyTip(i18N.Translate("Start downloading new version"));
NewVersionLabel.Enabled = false;
NewVersionLabel.Text = "...";
try
{
void OnDownloadProgressChanged(object o1, DownloadProgressChangedEventArgs args)
{
BeginInvoke(new Action(() => { NewVersionLabel.Text = $"{args.ProgressPercentage}%"; }));
}
await Updater.Updater.DownloadAndUpdate(Path.Combine(Global.NetchDir, "data"), Global.NetchDir, OnDownloadProgressChanged);
UpdateChecker.NewVersionFound += OnUpdateCheckerOnNewVersionFound;
UpdateChecker.Check(Global.Settings.CheckBetaUpdate).Wait();
}
catch (Exception exception)
finally
{
Logging.Error(exception.Message);
NotifyTip(exception.Message);
UpdateChecker.NewVersionFound -= OnUpdateCheckerOnNewVersionFound;
}
void OnUpdateCheckerOnNewVersionFound(object o, EventArgs eventArgs)
{
NotifyTip($"{i18N.Translate(@"New version available", ": ")}{UpdateChecker.LatestVersionNumber}");
NewVersionLabel.Text = i18N.Translate("New version available");
NewVersionLabel.Enabled = true;
NewVersionLabel.Visible = true;
}
}

View File

@@ -13,5 +13,15 @@ namespace Netch.Forms.Mode
return fileName.ToString();
}
public static string GetCustomModeRelativePath(string name)
{
if (name == string.Empty)
return string.Empty;
var safeFileName = ToSafeFileName(name);
var relativePath = $"Custom\\{safeFileName}.txt";
return relativePath;
}
}
}

View File

@@ -31,129 +31,40 @@ namespace Netch.Forms.Mode
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Process));
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
this.UseCustomFilenameBox = new System.Windows.Forms.CheckBox();
this.RemarkLabel = new System.Windows.Forms.Label();
this.RemarkTextBox = new System.Windows.Forms.TextBox();
this.FilenameLabel = new System.Windows.Forms.Label();
this.FilenameTextBox = new System.Windows.Forms.TextBox();
this.ScanButton = new System.Windows.Forms.Button();
this.ProcessGroupBox = new System.Windows.Forms.GroupBox();
this.AddButton = new System.Windows.Forms.Button();
this.ProcessNameTextBox = new System.Windows.Forms.TextBox();
this.RuleListBox = new System.Windows.Forms.ListBox();
this.RemarkTextBox = new System.Windows.Forms.TextBox();
this.RemarkLabel = new System.Windows.Forms.Label();
this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
this.ControlButton = new System.Windows.Forms.Button();
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.containerControl1 = new System.Windows.Forms.ContainerControl();
this.RuleRichTextBox = new System.Windows.Forms.RichTextBox();
this.ProcessGroupBox = new System.Windows.Forms.GroupBox();
this.SelectButton = new System.Windows.Forms.Button();
this.ScanButton = new System.Windows.Forms.Button();
this.ValidationButton = new System.Windows.Forms.Button();
this.ControlButton = new System.Windows.Forms.Button();
this.ConfigurationGroupBox.SuspendLayout();
this.ProcessGroupBox.SuspendLayout();
this.contextMenuStrip.SuspendLayout();
this.containerControl1.SuspendLayout();
this.ProcessGroupBox.SuspendLayout();
this.SuspendLayout();
//
// ConfigurationGroupBox
//
this.ConfigurationGroupBox.Controls.Add(this.containerControl1);
this.ConfigurationGroupBox.Controls.Add(this.UseCustomFilenameBox);
this.ConfigurationGroupBox.Controls.Add(this.RemarkLabel);
this.ConfigurationGroupBox.Controls.Add(this.RemarkTextBox);
this.ConfigurationGroupBox.Controls.Add(this.FilenameLabel);
this.ConfigurationGroupBox.Controls.Add(this.FilenameTextBox);
this.ConfigurationGroupBox.Controls.Add(this.ScanButton);
this.ConfigurationGroupBox.Controls.Add(this.containerControl1);
this.ConfigurationGroupBox.Controls.Add(this.ProcessGroupBox);
this.ConfigurationGroupBox.Controls.Add(this.RemarkTextBox);
this.ConfigurationGroupBox.Controls.Add(this.RemarkLabel);
this.ConfigurationGroupBox.Location = new System.Drawing.Point(12, 12);
this.ConfigurationGroupBox.Controls.Add(this.ControlButton);
this.ConfigurationGroupBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.ConfigurationGroupBox.Location = new System.Drawing.Point(12, 5);
this.ConfigurationGroupBox.Name = "ConfigurationGroupBox";
this.ConfigurationGroupBox.Size = new System.Drawing.Size(340, 344);
this.ConfigurationGroupBox.Size = new System.Drawing.Size(431, 378);
this.ConfigurationGroupBox.TabIndex = 0;
this.ConfigurationGroupBox.TabStop = false;
this.ConfigurationGroupBox.Text = "Configuration";
//
// UseCustomFilenameBox
//
this.UseCustomFilenameBox.AutoSize = true;
this.UseCustomFilenameBox.Location = new System.Drawing.Point(84, 76);
this.UseCustomFilenameBox.Name = "UseCustomFilenameBox";
this.UseCustomFilenameBox.Size = new System.Drawing.Size(152, 21);
this.UseCustomFilenameBox.TabIndex = 9;
this.UseCustomFilenameBox.Text = "Use Custom Filename";
this.UseCustomFilenameBox.UseVisualStyleBackColor = true;
//
// FilenameLabel
//
this.FilenameLabel.AutoSize = true;
this.FilenameLabel.Location = new System.Drawing.Point(12, 55);
this.FilenameLabel.Name = "FilenameLabel";
this.FilenameLabel.Size = new System.Drawing.Size(59, 17);
this.FilenameLabel.TabIndex = 6;
this.FilenameLabel.Text = "Filename";
//
// FilenameTextBox
//
this.FilenameTextBox.Location = new System.Drawing.Point(84, 52);
this.FilenameTextBox.Name = "FilenameTextBox";
this.FilenameTextBox.Size = new System.Drawing.Size(250, 23);
this.FilenameTextBox.TabIndex = 5;
this.FilenameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.UseCustomFilenameBox, "Checked", true));;
//
// ScanButton
//
this.ScanButton.Location = new System.Drawing.Point(6, 315);
this.ScanButton.Name = "ScanButton";
this.ScanButton.Size = new System.Drawing.Size(75, 23);
this.ScanButton.TabIndex = 4;
this.ScanButton.Text = "Scan";
this.ScanButton.UseVisualStyleBackColor = true;
this.ScanButton.Click += new System.EventHandler(this.ScanButton_Click);
//
// ProcessGroupBox
//
this.ProcessGroupBox.Controls.Add(this.AddButton);
this.ProcessGroupBox.Controls.Add(this.ProcessNameTextBox);
this.ProcessGroupBox.Location = new System.Drawing.Point(6, 263);
this.ProcessGroupBox.Name = "ProcessGroupBox";
this.ProcessGroupBox.Size = new System.Drawing.Size(328, 46);
this.ProcessGroupBox.TabIndex = 3;
this.ProcessGroupBox.TabStop = false;
//
// AddButton
//
this.AddButton.Location = new System.Drawing.Point(247, 15);
this.AddButton.Name = "AddButton";
this.AddButton.Size = new System.Drawing.Size(75, 23);
this.AddButton.TabIndex = 1;
this.AddButton.Text = "Add";
this.AddButton.UseVisualStyleBackColor = true;
this.AddButton.Click += new System.EventHandler(this.AddButton_Click);
//
// ProcessNameTextBox
//
this.ProcessNameTextBox.Location = new System.Drawing.Point(6, 15);
this.ProcessNameTextBox.Name = "ProcessNameTextBox";
this.ProcessNameTextBox.Size = new System.Drawing.Size(222, 23);
this.ProcessNameTextBox.TabIndex = 0;
//
// RuleListBox
//
this.RuleListBox.FormattingEnabled = true;
this.RuleListBox.Dock = DockStyle.Fill;
this.RuleListBox.ItemHeight = 17;
this.RuleListBox.Location = new System.Drawing.Point(0, 0);
this.RuleListBox.Name = "RuleListBox";
this.RuleListBox.Size = new System.Drawing.Size(328, 157);
this.RuleListBox.TabIndex = 2;
this.RuleListBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.RuleListBox_MouseUp);
//
// RemarkTextBox
//
this.RemarkTextBox.Location = new System.Drawing.Point(84, 22);
this.RemarkTextBox.Name = "RemarkTextBox";
this.RemarkTextBox.Size = new System.Drawing.Size(250, 23);
this.RemarkTextBox.TabIndex = 1;
this.RemarkTextBox.TextChanged += new System.EventHandler(this.RemarkTextBox_TextChanged);
//
// RemarkLabel
//
this.RemarkLabel.AutoSize = true;
@@ -163,79 +74,138 @@ namespace Netch.Forms.Mode
this.RemarkLabel.TabIndex = 0;
this.RemarkLabel.Text = "Remark";
//
// contextMenuStrip
// RemarkTextBox
//
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {this.DeleteToolStripMenuItem});
this.contextMenuStrip.Name = "contextMenuStrip";
this.contextMenuStrip.Size = new System.Drawing.Size(153, 48);
this.RemarkTextBox.Location = new System.Drawing.Point(84, 22);
this.RemarkTextBox.Name = "RemarkTextBox";
this.RemarkTextBox.Size = new System.Drawing.Size(341, 23);
this.RemarkTextBox.TabIndex = 1;
this.RemarkTextBox.TextChanged += new System.EventHandler(this.RemarkTextBox_TextChanged);
//
// ControlButton
// FilenameLabel
//
this.ControlButton.Location = new System.Drawing.Point(277, 362);
this.ControlButton.Name = "ControlButton";
this.ControlButton.Size = new System.Drawing.Size(75, 23);
this.ControlButton.TabIndex = 1;
this.ControlButton.Text = "Save";
this.ControlButton.UseVisualStyleBackColor = true;
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
this.FilenameLabel.AutoSize = true;
this.FilenameLabel.Location = new System.Drawing.Point(12, 55);
this.FilenameLabel.Name = "FilenameLabel";
this.FilenameLabel.Size = new System.Drawing.Size(59, 17);
this.FilenameLabel.TabIndex = 2;
this.FilenameLabel.Text = "Filename";
//
// DeleteToolStripMenuItem
// FilenameTextBox
//
this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem";
this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
this.DeleteToolStripMenuItem.Text = "Delete";
this.DeleteToolStripMenuItem.Click += new System.EventHandler(this.deleteRule_Click);
this.FilenameTextBox.Location = new System.Drawing.Point(84, 52);
this.FilenameTextBox.Name = "FilenameTextBox";
this.FilenameTextBox.ReadOnly = true;
this.FilenameTextBox.Size = new System.Drawing.Size(341, 23);
this.FilenameTextBox.TabIndex = 3;
//
// containerControl1
//
this.containerControl1.Controls.Add(this.RuleListBox);
this.containerControl1.Location = new System.Drawing.Point(6, 100);
this.containerControl1.Controls.Add(this.RuleRichTextBox);
this.containerControl1.Location = new System.Drawing.Point(6, 81);
this.containerControl1.Name = "containerControl1";
this.containerControl1.Size = new System.Drawing.Size(328, 157);
this.containerControl1.TabIndex = 10;
this.containerControl1.Padding = new Padding(0);
this.containerControl1.Size = new System.Drawing.Size(419, 221);
this.containerControl1.TabIndex = 4;
this.containerControl1.Text = "containerControl1";
//
// RuleRichTextBox
//
this.RuleRichTextBox.DetectUrls = false;
this.RuleRichTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.RuleRichTextBox.Location = new System.Drawing.Point(0, 0);
this.RuleRichTextBox.Name = "RuleRichTextBox";
this.RuleRichTextBox.Size = new System.Drawing.Size(419, 221);
this.RuleRichTextBox.TabIndex = 0;
this.RuleRichTextBox.Text = "";
this.RuleRichTextBox.WordWrap = false;
//
// ProcessGroupBox
//
this.ProcessGroupBox.Controls.Add(this.SelectButton);
this.ProcessGroupBox.Controls.Add(this.ScanButton);
this.ProcessGroupBox.Controls.Add(this.ValidationButton);
this.ProcessGroupBox.Location = new System.Drawing.Point(6, 295);
this.ProcessGroupBox.Name = "ProcessGroupBox";
this.ProcessGroupBox.Size = new System.Drawing.Size(419, 44);
this.ProcessGroupBox.TabIndex = 5;
this.ProcessGroupBox.TabStop = false;
//
// SelectButton
//
this.SelectButton.Location = new System.Drawing.Point(6, 13);
this.SelectButton.Name = "SelectButton";
this.SelectButton.Size = new System.Drawing.Size(75, 23);
this.SelectButton.TabIndex = 0;
this.SelectButton.Text = "Select";
this.SelectButton.UseVisualStyleBackColor = true;
this.SelectButton.Click += new System.EventHandler(this.SelectButton_Click);
//
// ScanButton
//
this.ScanButton.Location = new System.Drawing.Point(87, 13);
this.ScanButton.Name = "ScanButton";
this.ScanButton.Size = new System.Drawing.Size(75, 23);
this.ScanButton.TabIndex = 1;
this.ScanButton.Text = "Scan";
this.ScanButton.UseVisualStyleBackColor = true;
this.ScanButton.Click += new System.EventHandler(this.ScanButton_Click);
//
// ValidationButton
//
this.ValidationButton.Location = new System.Drawing.Point(338, 13);
this.ValidationButton.Name = "ValidationButton";
this.ValidationButton.Size = new System.Drawing.Size(75, 23);
this.ValidationButton.TabIndex = 2;
this.ValidationButton.Text = "Validation";
this.ValidationButton.UseVisualStyleBackColor = true;
this.ValidationButton.Click += new System.EventHandler(this.ValidationButton_Click);
//
// ControlButton
//
this.ControlButton.Location = new System.Drawing.Point(344, 345);
this.ControlButton.Name = "ControlButton";
this.ControlButton.Size = new System.Drawing.Size(75, 23);
this.ControlButton.TabIndex = 6;
this.ControlButton.Text = "Save";
this.ControlButton.UseVisualStyleBackColor = true;
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
//
// Process
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.ClientSize = new System.Drawing.Size(364, 397);
this.Controls.Add(this.ControlButton);
this.ClientSize = new System.Drawing.Size(455, 388);
this.Controls.Add(this.ConfigurationGroupBox);
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, ((byte)(134)));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.Name = "Process";
this.Padding = new System.Windows.Forms.Padding(12, 5, 12, 5);
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Create Process Mode";
this.Load += new System.EventHandler(this.ModeForm_Load);
this.ConfigurationGroupBox.ResumeLayout(false);
this.ConfigurationGroupBox.PerformLayout();
this.ProcessGroupBox.ResumeLayout(false);
this.ProcessGroupBox.PerformLayout();
this.contextMenuStrip.ResumeLayout(false);
this.containerControl1.ResumeLayout(false);
this.ProcessGroupBox.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button ScanButton;
private System.Windows.Forms.ContainerControl containerControl1;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip;
private System.Windows.Forms.ToolStripMenuItem DeleteToolStripMenuItem;
public System.Windows.Forms.GroupBox ConfigurationGroupBox;
private System.Windows.Forms.Label RemarkLabel;
private System.Windows.Forms.GroupBox ProcessGroupBox;
private System.Windows.Forms.ListBox RuleListBox;
private System.Windows.Forms.TextBox RemarkTextBox;
private System.Windows.Forms.TextBox ProcessNameTextBox;
private System.Windows.Forms.Button AddButton;
private System.Windows.Forms.Button ScanButton;
private System.Windows.Forms.Button SelectButton;
public System.Windows.Forms.Button ControlButton;
private System.Windows.Forms.Label FilenameLabel;
private System.Windows.Forms.TextBox FilenameTextBox;
private System.Windows.Forms.CheckBox UseCustomFilenameBox;
private RichTextBox RuleRichTextBox;
private Button ValidationButton;
}
}

View File

@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.WindowsAPICodePack.Dialogs;
using Netch.Controllers;
using Netch.Models;
using Netch.Properties;
using Netch.Utils;
@@ -31,103 +33,48 @@ namespace Netch.Forms.Mode
CheckForIllegalCrossThreadCalls = false;
_mode = mode;
if (mode != null)
}
#region Model
public IEnumerable<string> Rules => RuleRichTextBox.Lines;
private void RuleAdd(string value)
{
RuleRichTextBox.AppendText($"{value}\n");
}
private void RuleAddRange(IEnumerable<string> value)
{
foreach (string s in value)
{
RuleAdd(s);
}
}
#endregion
public void ModeForm_Load(object sender, EventArgs e)
{
if (_mode != null)
{
Text = "Edit Process Mode";
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
FilenameTextBox.Enabled = UseCustomFilenameBox.Enabled = false;
RemarkTextBox.Text = mode.Remark;
FilenameTextBox.Text = mode.RelativePath;
RuleListBox.Items.AddRange(mode.Rule.ToArray());
RemarkTextBox.Text = _mode.Remark;
FilenameTextBox.Text = _mode.RelativePath;
RuleAddRange(_mode.Rule);
}
}
/// <summary>
/// 是否被编辑过
/// </summary>
public bool Edited { get; private set; }
/// <summary>
/// 扫描目录
/// </summary>
/// <param name="DirName">路径</param>
public void ScanDirectory(string DirName)
{
try
{
RuleListBox.Items.AddRange(Directory.GetFiles(DirName, "*.exe", SearchOption.AllDirectories)
.Select(f => Path.GetFileName(f))
.ToArray());
}
catch (Exception)
{
// ignored
}
}
public void ModeForm_Load(object sender, EventArgs e)
{
i18N.TranslateForm(this);
i18N.Translate(contextMenuStrip);
}
/// <summary>
/// listBox右键菜单
/// </summary>
private void RuleListBox_MouseUp(object sender, MouseEventArgs e)
{
RuleListBox.SelectedIndex = RuleListBox.IndexFromPoint(e.X, e.Y);
if (RuleListBox.SelectedIndex == -1)
return;
if (e.Button == MouseButtons.Right)
contextMenuStrip.Show(RuleListBox, e.Location);
}
private void deleteRule_Click(object sender, EventArgs e)
{
if (RuleListBox.SelectedIndex == -1)
return;
RuleListBox.Items.RemoveAt(RuleListBox.SelectedIndex);
Edited = true;
}
private async void AddButton_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
if (string.IsNullOrWhiteSpace(ProcessNameTextBox.Text))
{
MessageBoxX.Show(i18N.Translate("Please enter an process name (xxx.exe)"));
return;
}
if (!NFController.CheckCppRegex(ProcessNameTextBox.Text))
{
MessageBoxX.Show("Rule does not conform to C++ regular expression syntax");
return;
}
var process = ProcessNameTextBox.Text;
if (!RuleListBox.Items.Contains(process))
RuleListBox.Items.Add(process);
Edited = true;
RuleListBox.SelectedIndex = RuleListBox.Items.IndexOf(process);
ProcessNameTextBox.Text = string.Empty;
});
}
private void ScanButton_Click(object sender, EventArgs e)
private void SelectButton_Click(object sender, EventArgs e)
{
var dialog = new CommonOpenFileDialog
{
IsFolderPicker = true,
Multiselect = false,
Multiselect = true,
Title = i18N.Translate("Select a folder"),
AddToMostRecentlyUsedList = false,
EnsurePathExists = true,
@@ -136,14 +83,20 @@ namespace Netch.Forms.Mode
if (dialog.ShowDialog(Handle) == CommonFileDialogResult.Ok)
{
ScanDirectory(dialog.FileName);
MessageBoxX.Show(i18N.Translate("Scan completed"));
foreach (string p in dialog.FileNames)
{
string path = p;
if (!path.EndsWith(@"\"))
path += @"\";
RuleAdd($"^{path.ToRegexString()}");
}
}
}
public void ControlButton_Click(object sender, EventArgs e)
{
if (RuleListBox.Items.Count == 0)
if (!RuleRichTextBox.Lines.Any())
{
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
return;
@@ -165,16 +118,14 @@ namespace Netch.Forms.Mode
{
_mode.Remark = RemarkTextBox.Text;
_mode.Rule.Clear();
_mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
_mode.Rule.AddRange(RuleRichTextBox.Lines);
_mode.WriteFile();
Global.MainForm.LoadModes();
Edited = false;
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
}
else
{
var relativePath = $"Custom\\{FilenameTextBox.Text}.txt";
var relativePath = FilenameTextBox.Text;
var fullName = ModeHelper.GetFullPath(relativePath);
if (File.Exists(fullName))
{
@@ -189,25 +140,73 @@ namespace Netch.Forms.Mode
Remark = RemarkTextBox.Text
};
mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
mode.Rule.AddRange(RuleRichTextBox.Lines);
mode.WriteFile();
ModeHelper.Add(mode);
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
}
Close();
}
private async void RemarkTextBox_TextChanged(object sender, EventArgs e)
private void RemarkTextBox_TextChanged(object sender, EventArgs e)
{
await Task.Run(() =>
BeginInvoke(new Action(() =>
{
if (!UseCustomFilenameBox.Checked)
FilenameTextBox.Text = FilenameTextBox.Text = ModeEditorUtils.GetCustomModeRelativePath(RemarkTextBox.Text);
}));
}
private void ScanButton_Click(object sender, EventArgs e)
{
var dialog = new CommonOpenFileDialog
{
IsFolderPicker = true,
Multiselect = false,
Title = i18N.Translate("Select a folder"),
AddToMostRecentlyUsedList = false,
EnsurePathExists = true,
NavigateToShortcut = true
};
if (dialog.ShowDialog(Handle) == CommonFileDialogResult.Ok)
{
var path = dialog.FileName;
var list = new List<string>();
const uint maxCount = 50;
try
{
FilenameTextBox.Text = ModeEditorUtils.ToSafeFileName(RemarkTextBox.Text);
ScanDirectory(path, list);
}
});
catch
{
MessageBoxX.Show(i18N.Translate($"The number of executable files in the \"{path}\" directory is greater than {maxCount}"),
LogLevel.WARNING);
return;
}
RuleAddRange(list);
}
}
private void ScanDirectory(string directory, List<string> list, uint maxCount = 30)
{
foreach (string dir in Directory.GetDirectories(directory))
ScanDirectory(dir, list, maxCount);
list.AddRange(Directory.GetFiles(directory).Select(Path.GetFileName).Where(s => s.EndsWith(".exe")).Select(s => s.ToRegexString()));
if (maxCount != 0 && list.Count > maxCount)
throw new Exception("The number of results is greater than maxCount");
}
private void ValidationButton_Click(object sender, EventArgs e)
{
if (NFController.CheckRules(Rules, out var results))
MessageBoxX.Show(NFController.GenerateInvalidRulesMessage(results), LogLevel.WARNING);
else
MessageBoxX.Show("Fine");
}
}
}

View File

@@ -35,7 +35,6 @@ namespace Netch.Forms.Mode
this.components = new System.ComponentModel.Container();
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
this.comboBox1 = new System.Windows.Forms.ComboBox();
this.UseCustomFilenameBox = new System.Windows.Forms.CheckBox();
this.FilenameLabel = new System.Windows.Forms.Label();
this.FilenameTextBox = new System.Windows.Forms.TextBox();
this.ActionLabel = new System.Windows.Forms.Label();
@@ -54,7 +53,6 @@ namespace Netch.Forms.Mode
// ConfigurationGroupBox
//
this.ConfigurationGroupBox.Controls.Add(this.comboBox1);
this.ConfigurationGroupBox.Controls.Add(this.UseCustomFilenameBox);
this.ConfigurationGroupBox.Controls.Add(this.FilenameLabel);
this.ConfigurationGroupBox.Controls.Add(this.FilenameTextBox);
this.ConfigurationGroupBox.Controls.Add(this.ActionLabel);
@@ -77,16 +75,6 @@ namespace Netch.Forms.Mode
this.comboBox1.Size = new System.Drawing.Size(138, 20);
this.comboBox1.TabIndex = 11;
//
// UseCustomFilenameBox
//
this.UseCustomFilenameBox.AutoSize = true;
this.UseCustomFilenameBox.Location = new System.Drawing.Point(84, 100);
this.UseCustomFilenameBox.Name = "UseCustomFilenameBox";
this.UseCustomFilenameBox.Size = new System.Drawing.Size(138, 16);
this.UseCustomFilenameBox.TabIndex = 9;
this.UseCustomFilenameBox.Text = "Use Custom Filename";
this.UseCustomFilenameBox.UseVisualStyleBackColor = true;
//
// FilenameLabel
//
this.FilenameLabel.AutoSize = true;
@@ -98,9 +86,9 @@ namespace Netch.Forms.Mode
//
// FilenameTextBox
//
this.FilenameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.UseCustomFilenameBox, "Checked", true));
this.FilenameTextBox.Location = new System.Drawing.Point(84, 76);
this.FilenameTextBox.Name = "FilenameTextBox";
this.FilenameTextBox.ReadOnly = true;
this.FilenameTextBox.Size = new System.Drawing.Size(250, 21);
this.FilenameTextBox.TabIndex = 5;
//
@@ -133,9 +121,9 @@ namespace Netch.Forms.Mode
// containerControl1
//
this.containerControl1.Controls.Add(this.richTextBox1);
this.containerControl1.Location = new System.Drawing.Point(6, 125);
this.containerControl1.Location = new System.Drawing.Point(6, 103);
this.containerControl1.Name = "containerControl1";
this.containerControl1.Size = new System.Drawing.Size(328, 224);
this.containerControl1.Size = new System.Drawing.Size(328, 246);
this.containerControl1.TabIndex = 10;
this.containerControl1.Text = "containerControl1";
//
@@ -144,13 +132,14 @@ namespace Netch.Forms.Mode
this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.richTextBox1.Location = new System.Drawing.Point(0, 0);
this.richTextBox1.Name = "richTextBox1";
this.richTextBox1.Size = new System.Drawing.Size(328, 224);
this.richTextBox1.Size = new System.Drawing.Size(328, 246);
this.richTextBox1.TabIndex = 0;
this.richTextBox1.Text = "";
//
// contextMenuStrip
//
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {this.DeleteToolStripMenuItem});
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.DeleteToolStripMenuItem});
this.contextMenuStrip.Name = "contextMenuStrip";
this.contextMenuStrip.Size = new System.Drawing.Size(114, 26);
//
@@ -186,6 +175,7 @@ namespace Netch.Forms.Mode
this.containerControl1.ResumeLayout(false);
this.contextMenuStrip.ResumeLayout(false);
this.ResumeLayout(false);
}
public System.Windows.Forms.GroupBox ConfigurationGroupBox;
@@ -198,7 +188,6 @@ namespace Netch.Forms.Mode
private System.Windows.Forms.Label RemarkLabel;
private System.Windows.Forms.TextBox RemarkTextBox;
private System.Windows.Forms.RichTextBox richTextBox1;
private System.Windows.Forms.CheckBox UseCustomFilenameBox;
#endregion

View File

@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Properties;
using Netch.Utils;
@@ -44,8 +43,6 @@ namespace Netch.Forms.Mode
comboBox1.DataSource = _items;
comboBox1.ValueMember = "Value";
comboBox1.DisplayMember = "Text";
i18N.TranslateForm(this);
}
private void Route_Load(object sender, EventArgs e)
@@ -55,13 +52,13 @@ namespace Netch.Forms.Mode
Text = "Edit Route Table Rule";
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
FilenameTextBox.Enabled = UseCustomFilenameBox.Enabled = false;
RemarkTextBox.Text = _mode.Remark;
comboBox1.SelectedValue = _mode.Type; // ComboBox SelectedValue worked after ctor
FilenameTextBox.Text = _mode.RelativePath;
richTextBox1.Lines = _mode.Rule.ToArray();
}
i18N.TranslateForm(this);
}
private void ControlButton_Click(object sender, EventArgs e)
@@ -86,12 +83,11 @@ namespace Netch.Forms.Mode
_mode.Type = (int) comboBox1.SelectedValue;
_mode.WriteFile();
Global.MainForm.LoadModes();
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
}
else
{
var relativePath = $"Custom\\{FilenameTextBox.Text}.txt";
var relativePath = FilenameTextBox.Text;
var fullName = ModeHelper.GetFullPath(relativePath);
if (File.Exists(fullName))
{
@@ -108,22 +104,18 @@ namespace Netch.Forms.Mode
mode.Rule.AddRange(richTextBox1.Lines);
mode.WriteFile();
ModeHelper.Add(mode);
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
}
Close();
}
private async void RemarkTextBox_TextChanged(object sender, EventArgs e)
private void RemarkTextBox_TextChanged(object sender, EventArgs e)
{
await Task.Run(() =>
BeginInvoke(new Action(() =>
{
if (!UseCustomFilenameBox.Checked)
{
FilenameTextBox.Text = ModeEditorUtils.ToSafeFileName(RemarkTextBox.Text);
}
});
FilenameTextBox.Text = ModeEditorUtils.GetCustomModeRelativePath(RemarkTextBox.Text);
}));
}
}
}

View File

@@ -33,9 +33,6 @@ namespace Netch.Forms
{
this.TabControl = new System.Windows.Forms.TabControl();
this.GeneralTabPage = new System.Windows.Forms.TabPage();
this.ServerPingTypeLabel = new System.Windows.Forms.Label();
this.TCPingRadioBtn = new System.Windows.Forms.RadioButton();
this.ICMPingRadioBtn = new System.Windows.Forms.RadioButton();
this.PortGroupBox = new System.Windows.Forms.GroupBox();
this.Socks5PortLabel = new System.Windows.Forms.Label();
this.Socks5PortTextBox = new System.Windows.Forms.TextBox();
@@ -44,14 +41,16 @@ namespace Netch.Forms
this.RedirectorLabel = new System.Windows.Forms.Label();
this.RedirectorTextBox = new System.Windows.Forms.TextBox();
this.AllowDevicesCheckBox = new System.Windows.Forms.CheckBox();
this.BootShadowsocksFromDLLCheckBox = 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();
this.ProfileCountLabel = new System.Windows.Forms.Label();
this.ProfileCountTextBox = new System.Windows.Forms.TextBox();
this.StartedPingLabel = new System.Windows.Forms.Label();
this.DetectionTickLabel = new System.Windows.Forms.Label();
this.StartedPingIntervalTextBox = new System.Windows.Forms.TextBox();
this.DetectionTickTextBox = new System.Windows.Forms.TextBox();
this.StartedPingLabel = new System.Windows.Forms.Label();
this.StartedPingIntervalTextBox = new System.Windows.Forms.TextBox();
this.STUNServerLabel = new System.Windows.Forms.Label();
this.STUN_ServerComboBox = new System.Windows.Forms.ComboBox();
this.AclLabel = new System.Windows.Forms.Label();
@@ -59,12 +58,17 @@ namespace Netch.Forms
this.LanguageLabel = new System.Windows.Forms.Label();
this.LanguageComboBox = new System.Windows.Forms.ComboBox();
this.NFTabPage = new System.Windows.Forms.TabPage();
this.ProcessProxyProtocolComboBox = new System.Windows.Forms.ComboBox();
this.ModifySystemDNSCheckBox = new System.Windows.Forms.CheckBox();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.ProcessProxyProtocolLabel = new System.Windows.Forms.Label();
this.ModifiedDNSLabel = new System.Windows.Forms.Label();
this.ModifiedDNSTextBox = new System.Windows.Forms.TextBox();
this.ProcessProxyProtocolComboBox = new System.Windows.Forms.ComboBox();
this.DNSRedirectorCheckBox = new System.Windows.Forms.CheckBox();
this.RDRDNSLabel = new System.Windows.Forms.Label();
this.RDRDNSTextBox = new System.Windows.Forms.TextBox();
this.ICMPRedirectorCheckBox = new System.Windows.Forms.CheckBox();
this.RDRICMPLabel = new System.Windows.Forms.Label();
this.ModifiedICMPTextBox = new System.Windows.Forms.TextBox();
this.RedirectorSSCheckBox = new System.Windows.Forms.CheckBox();
this.ChildProcessHandleCheckBox = new System.Windows.Forms.CheckBox();
this.TAPTabPage = new System.Windows.Forms.TabPage();
this.TUNTAPGroupBox = new System.Windows.Forms.GroupBox();
this.TUNTAPAddressLabel = new System.Windows.Forms.Label();
@@ -80,8 +84,8 @@ namespace Netch.Forms
this.UseFakeDNSCheckBox = new System.Windows.Forms.CheckBox();
this.GlobalBypassIPsButton = new System.Windows.Forms.Button();
this.v2rayTabPage = new System.Windows.Forms.TabPage();
this.TLSAllowInsecureCheckBox = new System.Windows.Forms.CheckBox();
this.XrayConeCheckBox = new System.Windows.Forms.CheckBox();
this.TLSAllowInsecureCheckBox = new System.Windows.Forms.CheckBox();
this.UseMuxCheckBox = new System.Windows.Forms.CheckBox();
this.KCPGroupBox = new System.Windows.Forms.GroupBox();
this.mtuLabel = new System.Windows.Forms.Label();
@@ -119,6 +123,7 @@ namespace Netch.Forms
this.GeneralTabPage.SuspendLayout();
this.PortGroupBox.SuspendLayout();
this.NFTabPage.SuspendLayout();
this.groupBox1.SuspendLayout();
this.TAPTabPage.SuspendLayout();
this.TUNTAPGroupBox.SuspendLayout();
this.v2rayTabPage.SuspendLayout();
@@ -146,18 +151,17 @@ namespace Netch.Forms
// GeneralTabPage
//
this.GeneralTabPage.BackColor = System.Drawing.SystemColors.ButtonFace;
this.GeneralTabPage.Controls.Add(this.ServerPingTypeLabel);
this.GeneralTabPage.Controls.Add(this.TCPingRadioBtn);
this.GeneralTabPage.Controls.Add(this.ICMPingRadioBtn);
this.GeneralTabPage.Controls.Add(this.PortGroupBox);
this.GeneralTabPage.Controls.Add(this.BootShadowsocksFromDLLCheckBox);
this.GeneralTabPage.Controls.Add(this.ResolveServerHostnameCheckBox);
this.GeneralTabPage.Controls.Add(this.ServerPingTypeLabel);
this.GeneralTabPage.Controls.Add(this.ICMPingRadioBtn);
this.GeneralTabPage.Controls.Add(this.TCPingRadioBtn);
this.GeneralTabPage.Controls.Add(this.ProfileCountLabel);
this.GeneralTabPage.Controls.Add(this.ProfileCountTextBox);
this.GeneralTabPage.Controls.Add(this.StartedPingLabel);
this.GeneralTabPage.Controls.Add(this.DetectionTickLabel);
this.GeneralTabPage.Controls.Add(this.StartedPingIntervalTextBox);
this.GeneralTabPage.Controls.Add(this.DetectionTickTextBox);
this.GeneralTabPage.Controls.Add(this.StartedPingLabel);
this.GeneralTabPage.Controls.Add(this.StartedPingIntervalTextBox);
this.GeneralTabPage.Controls.Add(this.STUNServerLabel);
this.GeneralTabPage.Controls.Add(this.STUN_ServerComboBox);
this.GeneralTabPage.Controls.Add(this.AclLabel);
@@ -171,37 +175,6 @@ namespace Netch.Forms
this.GeneralTabPage.TabIndex = 0;
this.GeneralTabPage.Text = "General";
//
// ServerPingTypeLabel
//
this.ServerPingTypeLabel.AutoSize = true;
this.ServerPingTypeLabel.Location = new System.Drawing.Point(267, 66);
this.ServerPingTypeLabel.Name = "ServerPingTypeLabel";
this.ServerPingTypeLabel.Size = new System.Drawing.Size(89, 12);
this.ServerPingTypeLabel.TabIndex = 16;
this.ServerPingTypeLabel.Text = "ServerPingType";
//
// TCPingRadioBtn
//
this.TCPingRadioBtn.AutoSize = true;
this.TCPingRadioBtn.Location = new System.Drawing.Point(332, 85);
this.TCPingRadioBtn.Name = "TCPingRadioBtn";
this.TCPingRadioBtn.Size = new System.Drawing.Size(59, 16);
this.TCPingRadioBtn.TabIndex = 15;
this.TCPingRadioBtn.TabStop = true;
this.TCPingRadioBtn.Text = "TCPing";
this.TCPingRadioBtn.UseVisualStyleBackColor = true;
//
// ICMPingRadioBtn
//
this.ICMPingRadioBtn.AutoSize = true;
this.ICMPingRadioBtn.Location = new System.Drawing.Point(268, 85);
this.ICMPingRadioBtn.Name = "ICMPingRadioBtn";
this.ICMPingRadioBtn.Size = new System.Drawing.Size(65, 16);
this.ICMPingRadioBtn.TabIndex = 14;
this.ICMPingRadioBtn.TabStop = true;
this.ICMPingRadioBtn.Text = "ICMPing";
this.ICMPingRadioBtn.UseVisualStyleBackColor = true;
//
// PortGroupBox
//
this.PortGroupBox.Controls.Add(this.Socks5PortLabel);
@@ -280,33 +253,54 @@ namespace Netch.Forms
this.AllowDevicesCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.AllowDevicesCheckBox.UseVisualStyleBackColor = true;
//
// BootShadowsocksFromDLLCheckBox
//
this.BootShadowsocksFromDLLCheckBox.AutoSize = true;
this.BootShadowsocksFromDLLCheckBox.Location = new System.Drawing.Point(267, 15);
this.BootShadowsocksFromDLLCheckBox.Name = "BootShadowsocksFromDLLCheckBox";
this.BootShadowsocksFromDLLCheckBox.Size = new System.Drawing.Size(60, 16);
this.BootShadowsocksFromDLLCheckBox.TabIndex = 1;
this.BootShadowsocksFromDLLCheckBox.Text = "SS DLL";
this.BootShadowsocksFromDLLCheckBox.UseVisualStyleBackColor = true;
//
// ResolveServerHostnameCheckBox
//
this.ResolveServerHostnameCheckBox.AutoSize = true;
this.ResolveServerHostnameCheckBox.Location = new System.Drawing.Point(267, 37);
this.ResolveServerHostnameCheckBox.Location = new System.Drawing.Point(267, 15);
this.ResolveServerHostnameCheckBox.Name = "ResolveServerHostnameCheckBox";
this.ResolveServerHostnameCheckBox.Size = new System.Drawing.Size(162, 16);
this.ResolveServerHostnameCheckBox.TabIndex = 2;
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.Name = "ServerPingTypeLabel";
this.ServerPingTypeLabel.Size = new System.Drawing.Size(89, 12);
this.ServerPingTypeLabel.TabIndex = 2;
this.ServerPingTypeLabel.Text = "ServerPingType";
//
// ICMPingRadioBtn
//
this.ICMPingRadioBtn.AutoSize = true;
this.ICMPingRadioBtn.Location = new System.Drawing.Point(268, 63);
this.ICMPingRadioBtn.Name = "ICMPingRadioBtn";
this.ICMPingRadioBtn.Size = new System.Drawing.Size(65, 16);
this.ICMPingRadioBtn.TabIndex = 3;
this.ICMPingRadioBtn.TabStop = true;
this.ICMPingRadioBtn.Text = "ICMPing";
this.ICMPingRadioBtn.UseVisualStyleBackColor = true;
//
// TCPingRadioBtn
//
this.TCPingRadioBtn.AutoSize = true;
this.TCPingRadioBtn.Location = new System.Drawing.Point(332, 63);
this.TCPingRadioBtn.Name = "TCPingRadioBtn";
this.TCPingRadioBtn.Size = new System.Drawing.Size(59, 16);
this.TCPingRadioBtn.TabIndex = 4;
this.TCPingRadioBtn.TabStop = true;
this.TCPingRadioBtn.Text = "TCPing";
this.TCPingRadioBtn.UseVisualStyleBackColor = true;
//
// ProfileCountLabel
//
this.ProfileCountLabel.AutoSize = true;
this.ProfileCountLabel.Location = new System.Drawing.Point(12, 160);
this.ProfileCountLabel.Name = "ProfileCountLabel";
this.ProfileCountLabel.Size = new System.Drawing.Size(77, 12);
this.ProfileCountLabel.TabIndex = 3;
this.ProfileCountLabel.TabIndex = 5;
this.ProfileCountLabel.Text = "ProfileCount";
//
// ProfileCountTextBox
@@ -314,50 +308,50 @@ namespace Netch.Forms
this.ProfileCountTextBox.Location = new System.Drawing.Point(120, 157);
this.ProfileCountTextBox.Name = "ProfileCountTextBox";
this.ProfileCountTextBox.Size = new System.Drawing.Size(90, 21);
this.ProfileCountTextBox.TabIndex = 4;
this.ProfileCountTextBox.TabIndex = 6;
this.ProfileCountTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// StartedPingLabel
//
this.StartedPingLabel.AutoSize = true;
this.StartedPingLabel.Location = new System.Drawing.Point(12, 187);
this.StartedPingLabel.Name = "StartedPingLabel";
this.StartedPingLabel.Size = new System.Drawing.Size(137, 12);
this.StartedPingLabel.TabIndex = 5;
this.StartedPingLabel.Text = "Delay test after start";
//
// DetectionTickLabel
//
this.DetectionTickLabel.AutoSize = true;
this.DetectionTickLabel.Location = new System.Drawing.Point(225, 160);
this.DetectionTickLabel.Name = "DetectionTickLabel";
this.DetectionTickLabel.Size = new System.Drawing.Size(119, 12);
this.DetectionTickLabel.TabIndex = 6;
this.DetectionTickLabel.TabIndex = 7;
this.DetectionTickLabel.Text = "Detection Tick(sec)";
//
// StartedPingIntervalTextBox
//
this.StartedPingIntervalTextBox.Location = new System.Drawing.Point(177, 184);
this.StartedPingIntervalTextBox.Name = "StartedPingIntervalTextBox";
this.StartedPingIntervalTextBox.Size = new System.Drawing.Size(68, 21);
this.StartedPingIntervalTextBox.TabIndex = 7;
this.StartedPingIntervalTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// DetectionTickTextBox
//
this.DetectionTickTextBox.Location = new System.Drawing.Point(366, 157);
this.DetectionTickTextBox.Name = "DetectionTickTextBox";
this.DetectionTickTextBox.Size = new System.Drawing.Size(68, 21);
this.DetectionTickTextBox.TabIndex = 7;
this.DetectionTickTextBox.TabIndex = 8;
this.DetectionTickTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// StartedPingLabel
//
this.StartedPingLabel.AutoSize = true;
this.StartedPingLabel.Location = new System.Drawing.Point(12, 187);
this.StartedPingLabel.Name = "StartedPingLabel";
this.StartedPingLabel.Size = new System.Drawing.Size(137, 12);
this.StartedPingLabel.TabIndex = 9;
this.StartedPingLabel.Text = "Delay test after start";
//
// StartedPingIntervalTextBox
//
this.StartedPingIntervalTextBox.Location = new System.Drawing.Point(177, 184);
this.StartedPingIntervalTextBox.Name = "StartedPingIntervalTextBox";
this.StartedPingIntervalTextBox.Size = new System.Drawing.Size(68, 21);
this.StartedPingIntervalTextBox.TabIndex = 10;
this.StartedPingIntervalTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// STUNServerLabel
//
this.STUNServerLabel.AutoSize = true;
this.STUNServerLabel.Location = new System.Drawing.Point(12, 216);
this.STUNServerLabel.Name = "STUNServerLabel";
this.STUNServerLabel.Size = new System.Drawing.Size(71, 12);
this.STUNServerLabel.TabIndex = 8;
this.STUNServerLabel.TabIndex = 11;
this.STUNServerLabel.Text = "STUN Server";
//
// STUN_ServerComboBox
@@ -366,7 +360,7 @@ namespace Netch.Forms
this.STUN_ServerComboBox.Location = new System.Drawing.Point(120, 213);
this.STUN_ServerComboBox.Name = "STUN_ServerComboBox";
this.STUN_ServerComboBox.Size = new System.Drawing.Size(314, 20);
this.STUN_ServerComboBox.TabIndex = 9;
this.STUN_ServerComboBox.TabIndex = 12;
//
// AclLabel
//
@@ -374,7 +368,7 @@ namespace Netch.Forms
this.AclLabel.Location = new System.Drawing.Point(12, 248);
this.AclLabel.Name = "AclLabel";
this.AclLabel.Size = new System.Drawing.Size(65, 12);
this.AclLabel.TabIndex = 10;
this.AclLabel.TabIndex = 13;
this.AclLabel.Text = "Custom ACL";
//
// AclAddrTextBox
@@ -382,7 +376,7 @@ namespace Netch.Forms
this.AclAddrTextBox.Location = new System.Drawing.Point(120, 245);
this.AclAddrTextBox.Name = "AclAddrTextBox";
this.AclAddrTextBox.Size = new System.Drawing.Size(314, 21);
this.AclAddrTextBox.TabIndex = 11;
this.AclAddrTextBox.TabIndex = 14;
this.AclAddrTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// LanguageLabel
@@ -391,7 +385,7 @@ namespace Netch.Forms
this.LanguageLabel.Location = new System.Drawing.Point(12, 277);
this.LanguageLabel.Name = "LanguageLabel";
this.LanguageLabel.Size = new System.Drawing.Size(53, 12);
this.LanguageLabel.TabIndex = 12;
this.LanguageLabel.TabIndex = 15;
this.LanguageLabel.Text = "Language";
//
// LanguageComboBox
@@ -401,17 +395,14 @@ namespace Netch.Forms
this.LanguageComboBox.Location = new System.Drawing.Point(120, 274);
this.LanguageComboBox.Name = "LanguageComboBox";
this.LanguageComboBox.Size = new System.Drawing.Size(121, 20);
this.LanguageComboBox.TabIndex = 13;
this.LanguageComboBox.TabIndex = 16;
//
// NFTabPage
//
this.NFTabPage.BackColor = System.Drawing.SystemColors.ButtonFace;
this.NFTabPage.Controls.Add(this.ModifySystemDNSCheckBox);
this.NFTabPage.Controls.Add(this.ModifiedDNSLabel);
this.NFTabPage.Controls.Add(this.ModifiedDNSTextBox);
this.NFTabPage.Controls.Add(this.groupBox1);
this.NFTabPage.Controls.Add(this.RedirectorSSCheckBox);
this.NFTabPage.Controls.Add(this.ProcessProxyProtocolLabel);
this.NFTabPage.Controls.Add(this.ProcessProxyProtocolComboBox);
this.NFTabPage.Controls.Add(this.ChildProcessHandleCheckBox);
this.NFTabPage.Location = new System.Drawing.Point(4, 25);
this.NFTabPage.Name = "NFTabPage";
this.NFTabPage.Padding = new System.Windows.Forms.Padding(3);
@@ -419,62 +410,116 @@ namespace Netch.Forms
this.NFTabPage.TabIndex = 1;
this.NFTabPage.Text = "Process Mode";
//
// ProcessProxyProtocolComboBox
// groupBox1
//
this.ProcessProxyProtocolComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.ProcessProxyProtocolComboBox.FormattingEnabled = true;
this.ProcessProxyProtocolComboBox.Location = new System.Drawing.Point(167, 58);
this.ProcessProxyProtocolComboBox.Name = "ProcessProxyProtocolComboBox";
this.ProcessProxyProtocolComboBox.Size = new System.Drawing.Size(121, 20);
this.ProcessProxyProtocolComboBox.TabIndex = 3;
//
// ModifySystemDNSCheckBox
//
this.ModifySystemDNSCheckBox.AutoSize = true;
this.ModifySystemDNSCheckBox.Location = new System.Drawing.Point(8, 16);
this.ModifySystemDNSCheckBox.Name = "ModifySystemDNSCheckBox";
this.ModifySystemDNSCheckBox.Size = new System.Drawing.Size(126, 16);
this.ModifySystemDNSCheckBox.TabIndex = 0;
this.ModifySystemDNSCheckBox.Text = "Modify System DNS";
this.ModifySystemDNSCheckBox.UseVisualStyleBackColor = true;
this.groupBox1.Controls.Add(this.ProcessProxyProtocolLabel);
this.groupBox1.Controls.Add(this.ProcessProxyProtocolComboBox);
this.groupBox1.Controls.Add(this.DNSRedirectorCheckBox);
this.groupBox1.Controls.Add(this.RDRDNSLabel);
this.groupBox1.Controls.Add(this.RDRDNSTextBox);
this.groupBox1.Controls.Add(this.ICMPRedirectorCheckBox);
this.groupBox1.Controls.Add(this.RDRICMPLabel);
this.groupBox1.Controls.Add(this.ModifiedICMPTextBox);
this.groupBox1.Location = new System.Drawing.Point(5, 6);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(450, 117);
this.groupBox1.TabIndex = 0;
this.groupBox1.TabStop = false;
//
// ProcessProxyProtocolLabel
//
this.ProcessProxyProtocolLabel.AutoSize = true;
this.ProcessProxyProtocolLabel.Location = new System.Drawing.Point(24, 61);
this.ProcessProxyProtocolLabel.Location = new System.Drawing.Point(23, 21);
this.ProcessProxyProtocolLabel.Name = "ProcessProxyProtocolLabel";
this.ProcessProxyProtocolLabel.Size = new System.Drawing.Size(89, 12);
this.ProcessProxyProtocolLabel.TabIndex = 2;
this.ProcessProxyProtocolLabel.TabIndex = 0;
this.ProcessProxyProtocolLabel.Text = "Proxy Protocol";
//
// ModifiedDNSLabel
// ProcessProxyProtocolComboBox
//
this.ModifiedDNSLabel.AutoSize = true;
this.ModifiedDNSLabel.Location = new System.Drawing.Point(223, 17);
this.ModifiedDNSLabel.Name = "ModifiedDNSLabel";
this.ModifiedDNSLabel.Size = new System.Drawing.Size(23, 12);
this.ModifiedDNSLabel.TabIndex = 2;
this.ModifiedDNSLabel.Text = "DNS";
this.ProcessProxyProtocolComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.ProcessProxyProtocolComboBox.FormattingEnabled = true;
this.ProcessProxyProtocolComboBox.Location = new System.Drawing.Point(118, 16);
this.ProcessProxyProtocolComboBox.Name = "ProcessProxyProtocolComboBox";
this.ProcessProxyProtocolComboBox.Size = new System.Drawing.Size(191, 20);
this.ProcessProxyProtocolComboBox.TabIndex = 1;
//
// ModifiedDNSTextBox
// DNSRedirectorCheckBox
//
this.ModifiedDNSTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.ModifySystemDNSCheckBox, "Checked", true));
this.ModifiedDNSTextBox.Location = new System.Drawing.Point(264, 14);
this.ModifiedDNSTextBox.Name = "ModifiedDNSTextBox";
this.ModifiedDNSTextBox.Size = new System.Drawing.Size(194, 21);
this.ModifiedDNSTextBox.TabIndex = 1;
this.ModifiedDNSTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
this.DNSRedirectorCheckBox.AutoSize = true;
this.DNSRedirectorCheckBox.Location = new System.Drawing.Point(6, 51);
this.DNSRedirectorCheckBox.Name = "DNSRedirectorCheckBox";
this.DNSRedirectorCheckBox.Size = new System.Drawing.Size(108, 16);
this.DNSRedirectorCheckBox.TabIndex = 2;
this.DNSRedirectorCheckBox.Text = "DNS Redirector";
this.DNSRedirectorCheckBox.UseVisualStyleBackColor = true;
//
// RDRDNSLabel
//
this.RDRDNSLabel.AutoSize = true;
this.RDRDNSLabel.Location = new System.Drawing.Point(224, 52);
this.RDRDNSLabel.Name = "RDRDNSLabel";
this.RDRDNSLabel.Size = new System.Drawing.Size(23, 12);
this.RDRDNSLabel.TabIndex = 3;
this.RDRDNSLabel.Text = "DNS";
//
// RDRDNSTextBox
//
this.RDRDNSTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.DNSRedirectorCheckBox, "Checked", true));
this.RDRDNSTextBox.Location = new System.Drawing.Point(253, 46);
this.RDRDNSTextBox.Name = "RDRDNSTextBox";
this.RDRDNSTextBox.Size = new System.Drawing.Size(191, 21);
this.RDRDNSTextBox.TabIndex = 4;
this.RDRDNSTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// ICMPRedirectorCheckBox
//
this.ICMPRedirectorCheckBox.AutoSize = true;
this.ICMPRedirectorCheckBox.Location = new System.Drawing.Point(6, 81);
this.ICMPRedirectorCheckBox.Name = "ICMPRedirectorCheckBox";
this.ICMPRedirectorCheckBox.Size = new System.Drawing.Size(114, 16);
this.ICMPRedirectorCheckBox.TabIndex = 5;
this.ICMPRedirectorCheckBox.Text = "ICMP Redirector";
this.ICMPRedirectorCheckBox.UseVisualStyleBackColor = true;
//
// RDRICMPLabel
//
this.RDRICMPLabel.AutoSize = true;
this.RDRICMPLabel.Location = new System.Drawing.Point(218, 81);
this.RDRICMPLabel.Name = "RDRICMPLabel";
this.RDRICMPLabel.Size = new System.Drawing.Size(29, 12);
this.RDRICMPLabel.TabIndex = 6;
this.RDRICMPLabel.Text = "ICMP";
//
// ModifiedICMPTextBox
//
this.ModifiedICMPTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.ICMPRedirectorCheckBox, "Checked", true));
this.ModifiedICMPTextBox.Location = new System.Drawing.Point(253, 78);
this.ModifiedICMPTextBox.Name = "ModifiedICMPTextBox";
this.ModifiedICMPTextBox.Size = new System.Drawing.Size(191, 21);
this.ModifiedICMPTextBox.TabIndex = 7;
this.ModifiedICMPTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// RedirectorSSCheckBox
//
this.RedirectorSSCheckBox.AutoSize = true;
this.RedirectorSSCheckBox.Location = new System.Drawing.Point(8, 38);
this.RedirectorSSCheckBox.Location = new System.Drawing.Point(11, 129);
this.RedirectorSSCheckBox.Name = "RedirectorSSCheckBox";
this.RedirectorSSCheckBox.Size = new System.Drawing.Size(102, 16);
this.RedirectorSSCheckBox.TabIndex = 0;
this.RedirectorSSCheckBox.TabIndex = 1;
this.RedirectorSSCheckBox.Text = "Redirector SS";
this.RedirectorSSCheckBox.UseVisualStyleBackColor = true;
//
// ChildProcessHandleCheckBox
//
this.ChildProcessHandleCheckBox.AutoSize = true;
this.ChildProcessHandleCheckBox.Location = new System.Drawing.Point(11, 151);
this.ChildProcessHandleCheckBox.Name = "ChildProcessHandleCheckBox";
this.ChildProcessHandleCheckBox.Size = new System.Drawing.Size(144, 16);
this.ChildProcessHandleCheckBox.TabIndex = 2;
this.ChildProcessHandleCheckBox.Text = "Child Process Handle";
this.ChildProcessHandleCheckBox.UseVisualStyleBackColor = true;
//
// TAPTabPage
//
this.TAPTabPage.BackColor = System.Drawing.SystemColors.ButtonFace;
@@ -621,8 +666,8 @@ namespace Netch.Forms
// v2rayTabPage
//
this.v2rayTabPage.BackColor = System.Drawing.SystemColors.ButtonFace;
this.v2rayTabPage.Controls.Add(this.TLSAllowInsecureCheckBox);
this.v2rayTabPage.Controls.Add(this.XrayConeCheckBox);
this.v2rayTabPage.Controls.Add(this.TLSAllowInsecureCheckBox);
this.v2rayTabPage.Controls.Add(this.UseMuxCheckBox);
this.v2rayTabPage.Controls.Add(this.KCPGroupBox);
this.v2rayTabPage.Location = new System.Drawing.Point(4, 25);
@@ -632,33 +677,33 @@ namespace Netch.Forms
this.v2rayTabPage.TabIndex = 3;
this.v2rayTabPage.Text = "V2Ray";
//
// TLSAllowInsecureCheckBox
//
this.TLSAllowInsecureCheckBox.AutoSize = true;
this.TLSAllowInsecureCheckBox.Location = new System.Drawing.Point(6, 42);
this.TLSAllowInsecureCheckBox.Name = "TLSAllowInsecureCheckBox";
this.TLSAllowInsecureCheckBox.Size = new System.Drawing.Size(126, 16);
this.TLSAllowInsecureCheckBox.TabIndex = 0;
this.TLSAllowInsecureCheckBox.Text = "TLS AllowInsecure";
this.TLSAllowInsecureCheckBox.UseVisualStyleBackColor = true;
//
// XrayConeCheckBox
//
this.XrayConeCheckBox.AutoSize = true;
this.XrayConeCheckBox.Location = new System.Drawing.Point(6, 15);
this.XrayConeCheckBox.Name = "XrayConeCheckBox";
this.XrayConeCheckBox.Size = new System.Drawing.Size(336, 16);
this.XrayConeCheckBox.TabIndex = 1;
this.XrayConeCheckBox.TabIndex = 0;
this.XrayConeCheckBox.Text = "FullCone Support (Required Server Xray-core v1.3.0+)";
this.XrayConeCheckBox.UseVisualStyleBackColor = true;
//
// TLSAllowInsecureCheckBox
//
this.TLSAllowInsecureCheckBox.AutoSize = true;
this.TLSAllowInsecureCheckBox.Location = new System.Drawing.Point(6, 42);
this.TLSAllowInsecureCheckBox.Name = "TLSAllowInsecureCheckBox";
this.TLSAllowInsecureCheckBox.Size = new System.Drawing.Size(126, 16);
this.TLSAllowInsecureCheckBox.TabIndex = 1;
this.TLSAllowInsecureCheckBox.Text = "TLS AllowInsecure";
this.TLSAllowInsecureCheckBox.UseVisualStyleBackColor = true;
//
// UseMuxCheckBox
//
this.UseMuxCheckBox.AutoSize = true;
this.UseMuxCheckBox.Location = new System.Drawing.Point(148, 42);
this.UseMuxCheckBox.Name = "UseMuxCheckBox";
this.UseMuxCheckBox.Size = new System.Drawing.Size(66, 16);
this.UseMuxCheckBox.TabIndex = 1;
this.UseMuxCheckBox.TabIndex = 2;
this.UseMuxCheckBox.Text = "Use Mux";
this.UseMuxCheckBox.UseVisualStyleBackColor = true;
//
@@ -680,7 +725,7 @@ namespace Netch.Forms
this.KCPGroupBox.Location = new System.Drawing.Point(9, 75);
this.KCPGroupBox.Name = "KCPGroupBox";
this.KCPGroupBox.Size = new System.Drawing.Size(427, 204);
this.KCPGroupBox.TabIndex = 2;
this.KCPGroupBox.TabIndex = 3;
this.KCPGroupBox.TabStop = false;
this.KCPGroupBox.Text = "KCP";
//
@@ -1012,6 +1057,8 @@ namespace Netch.Forms
this.PortGroupBox.PerformLayout();
this.NFTabPage.ResumeLayout(false);
this.NFTabPage.PerformLayout();
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.TAPTabPage.ResumeLayout(false);
this.TUNTAPGroupBox.ResumeLayout(false);
this.TUNTAPGroupBox.PerformLayout();
@@ -1047,7 +1094,6 @@ namespace Netch.Forms
private System.Windows.Forms.Label Socks5PortLabel;
private System.Windows.Forms.TextBox Socks5PortTextBox;
private System.Windows.Forms.CheckBox ResolveServerHostnameCheckBox;
private System.Windows.Forms.CheckBox BootShadowsocksFromDLLCheckBox;
private System.Windows.Forms.GroupBox TUNTAPGroupBox;
private System.Windows.Forms.CheckBox UseFakeDNSCheckBox;
private System.Windows.Forms.CheckBox ProxyDNSCheckBox;
@@ -1061,7 +1107,7 @@ namespace Netch.Forms
private System.Windows.Forms.Label TUNTAPAddressLabel;
private System.Windows.Forms.TextBox TUNTAPAddressTextBox;
private System.Windows.Forms.Button GlobalBypassIPsButton;
private System.Windows.Forms.CheckBox ModifySystemDNSCheckBox;
private System.Windows.Forms.CheckBox DNSRedirectorCheckBox;
private System.Windows.Forms.Button ControlButton;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
private System.Windows.Forms.TabPage OtherTabPage;
@@ -1107,13 +1153,18 @@ namespace Netch.Forms
private System.Windows.Forms.Label ChinaDNSLabel;
private System.Windows.Forms.TextBox OtherDNSTextBox;
private System.Windows.Forms.TextBox ChinaDNSTextBox;
private System.Windows.Forms.TextBox ModifiedDNSTextBox;
private System.Windows.Forms.Label ModifiedDNSLabel;
private System.Windows.Forms.TextBox RDRDNSTextBox;
private System.Windows.Forms.Label RDRDNSLabel;
private System.Windows.Forms.CheckBox RedirectorSSCheckBox;
private System.Windows.Forms.Label ServerPingTypeLabel;
private System.Windows.Forms.RadioButton TCPingRadioBtn;
private System.Windows.Forms.RadioButton ICMPingRadioBtn;
private System.Windows.Forms.ComboBox ProcessProxyProtocolComboBox;
private System.Windows.Forms.Label ProcessProxyProtocolLabel;
private System.Windows.Forms.CheckBox ICMPRedirectorCheckBox;
private System.Windows.Forms.TextBox ModifiedICMPTextBox;
private System.Windows.Forms.Label RDRICMPLabel;
private System.Windows.Forms.CheckBox ChildProcessHandleCheckBox;
private System.Windows.Forms.GroupBox groupBox1;
}
}

View File

@@ -44,7 +44,6 @@ namespace Netch.Forms
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(BootShadowsocksFromDLLCheckBox, c => Global.Settings.BootShadowsocksFromDLL = c, Global.Settings.BootShadowsocksFromDLL);
BindCheckBox(ResolveServerHostnameCheckBox, c => Global.Settings.ResolveServerHostname = c, Global.Settings.ResolveServerHostname);
BindRadioBox(ICMPingRadioBtn, _ => { }, !Global.Settings.ServerTCPing);
@@ -109,12 +108,18 @@ namespace Netch.Forms
#region Process Mode
BindCheckBox(ModifySystemDNSCheckBox, b => Global.Settings.ModifySystemDNS = b, Global.Settings.ModifySystemDNS);
BindCheckBox(DNSRedirectorCheckBox, b => Global.Settings.RedirectDNS = b, Global.Settings.RedirectDNS);
BindTextBox(ModifiedDNSTextBox, s => DnsUtils.TrySplit(s, out _, 2), s => Global.Settings.ModifiedDNS = s, Global.Settings.ModifiedDNS);
BindTextBox(RDRDNSTextBox, s => DnsUtils.TrySplit(s, out _, 2), s => Global.Settings.RedirectDNSAddr = s, Global.Settings.RedirectDNSAddr);
BindCheckBox(ICMPRedirectorCheckBox, b => Global.Settings.RedirectICMP = b, Global.Settings.RedirectICMP);
BindTextBox(ModifiedICMPTextBox, s => DnsUtils.TrySplit(s, out _, 2), s => Global.Settings.RedirectICMPAddr = s, Global.Settings.RedirectICMPAddr);
BindCheckBox(RedirectorSSCheckBox, s => Global.Settings.RedirectorSS = s, Global.Settings.RedirectorSS);
BindCheckBox(ChildProcessHandleCheckBox, s => Global.Settings.ChildProcessHandle = s, Global.Settings.ChildProcessHandle);
BindListComboBox(ProcessProxyProtocolComboBox,
s => Global.Settings.ProcessProxyProtocol = (PortType) Enum.Parse(typeof(PortType), s.ToString(), false),
Enum.GetNames(typeof(PortType)).Cast<object>().ToArray(),

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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>
</root>

View File

@@ -4,7 +4,6 @@ using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading;
using System.Windows.Forms;
using WindowsJobAPI;
using Netch.Controllers;
using Netch.Forms;
using Netch.Models;
@@ -30,13 +29,9 @@ namespace Netch
/// </summary>
private static readonly Lazy<MainForm> LazyMainForm = new(() => new MainForm());
public static readonly Mutex Mutex = new(false, "Global\\Netch");
private static readonly Lazy<Mutex> LazyMutex = new(() => new Mutex(false, "Global\\Netch"));
#if DEBUG
public static bool Testing = false;
#else
public const bool Testing = false;
#endif
public static Mutex Mutex => LazyMutex.Value;
/// <summary>
/// 用于读取和写入的配置
@@ -48,11 +43,6 @@ namespace Netch
/// </summary>
public static readonly List<Mode> Modes = new();
/// <summary>
/// Windows Job API
/// </summary>
public static readonly JobObject Job = new();
public static class Flags
{
public static readonly bool IsWindows10Upper = Environment.OSVersion.Version.Major >= 10;

View File

@@ -3,18 +3,48 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Netch.Controllers;
using Netch.Utils;
namespace Netch.Models
{
public class Mode
{
public readonly string? FullName;
private readonly Lazy<List<string>> _lazyRule;
public string? FullName { get; private set; }
public Mode(string? fullName = default)
{
_lazyRule = new Lazy<List<string>>(ReadRules);
if (fullName == null)
return;
FullName = fullName;
if (!File.Exists(FullName))
return;
var text = File.ReadLines(FullName).First();
// load head
if (text.First() != '#')
throw new Exception($"mode {FullName} head not found at Line 0");
var split = text.Substring(1).SplitTrimEntries(',');
Remark = split[0];
var typeResult = int.TryParse(split.ElementAtOrDefault(1), out var type);
Type = typeResult ? type : 0;
// TODO throw NotSupportedModeTypeException
var bypassChinaResult = int.TryParse(split.ElementAtOrDefault(2), out var bypassChina);
BypassChina = this.ClientRouting() && bypassChinaResult && bypassChina == 1;
}
/// <summary>
/// 规则
/// </summary>
public readonly List<string> Rule = new();
public List<string> Rule => _lazyRule.Value;
/// <summary>
/// 绕过中国0. 不绕过 1. 绕过)
@@ -44,15 +74,6 @@ namespace Netch.Models
/// </summary>
public int Type { get; set; } = 0;
public Mode(string fullName)
{
FullName = fullName;
}
public Mode()
{
}
/// <summary>
/// 文件相对路径(必须是存在的文件)
/// </summary>
@@ -78,30 +99,21 @@ namespace Netch.Models
relativePath.Replace(">", "");
relativePath.Replace(".h", ".txt");
var mode = Global.Modes.FirstOrDefault(m => m!.FullName != null && m.RelativePath!.Equals(relativePath.ToString()));
var mode = Global.Modes.FirstOrDefault(m => m.FullName != null && m.RelativePath!.Equals(relativePath.ToString()));
if (mode == null)
{
Logging.Warning($"{relativePath} file included in {Remark} not found");
}
else if (mode == this)
{
Logging.Warning("Can't self-reference");
}
else
{
if (mode.Type != Type)
{
Logging.Warning($"{mode.Remark}'s mode is not as same as {Remark}'s mode");
}
else
{
if (mode.Rule.Any(rule => rule.StartsWith("#include")))
Logging.Warning("Cannot reference mode that reference other mode");
else
result.AddRange(mode.FullRule);
}
}
throw new MessageException($"{relativePath} file included in {Remark} not found");
if (mode == this)
throw new MessageException("Can't self-reference");
if (mode.Type != Type)
throw new MessageException($"{mode.Remark}'s mode is not as same as {Remark}'s mode");
if (mode.Rule.Any(rule => rule.StartsWith("#include")))
throw new Exception("Cannot reference mode that reference other mode");
result.AddRange(mode.FullRule);
}
else
{
@@ -113,6 +125,14 @@ namespace Netch.Models
}
}
private List<string> ReadRules()
{
if (FullName == null || !File.Exists(FullName))
return new List<string>();
return File.ReadLines(FullName!).Skip(1).ToList();
}
public void WriteFile(string? fullName = null)
{
if (fullName != null)

View File

@@ -0,0 +1,121 @@
using System;
using System.Linq;
using System.Reflection;
using Netch.Utils;
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 ((value?.ToString() ?? null).IsNullOrWhiteSpace())
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

@@ -102,11 +102,6 @@ namespace Netch.Models
public AioDNSConfig AioDNS { get; set; } = new();
/// <summary>
/// 是否使用DLL启动Shadowsocks
/// </summary>
public bool BootShadowsocksFromDLL { get; set; } = true;
/// <summary>
/// 全局绕过 IP 列表
/// </summary>
@@ -158,14 +153,24 @@ namespace Netch.Models
public int ModeComboBoxSelectedIndex { get; set; } = 0;
/// <summary>
/// 要修改为的系统 DNS
/// 转发DNS地址
/// </summary>
public string ModifiedDNS { get; set; } = "1.1.1.1,8.8.8.8";
public string RedirectDNSAddr { get; set; } = "8.8.8.8";
/// <summary>
/// 修改系统 DNS
/// 是否开启DNS转发
/// </summary>
public bool ModifySystemDNS { get; set; } = false;
public bool RedirectDNS { get; set; } = false;
/// <summary>
/// 转发ICMP地址
/// </summary>
public string RedirectICMPAddr { get; set; } = "1.2.4.8";
/// <summary>
/// 是否开启ICMP转发
/// </summary>
public bool RedirectICMP { get; set; } = false;
/// <summary>
/// GFWList
@@ -202,6 +207,11 @@ namespace Netch.Models
/// </summary>
public bool RedirectorSS { get; set; } = false;
/// <summary>
/// 是否代理子进程
/// </summary>
public bool ChildProcessHandle { get; set; } = false;
/// <summary>
/// Redirector TCP 占用端口
/// </summary>
@@ -272,11 +282,6 @@ namespace Netch.Models
/// </summary>
public TUNTAPConfig TUNTAP { get; set; } = new();
/// <summary>
/// UDP Socket 占用端口
/// </summary>
public ushort UDPSocketPort { get; set; } = 18291;
/// <summary>
/// 是否打开软件时更新订阅
/// </summary>

View File

@@ -1,26 +1,56 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Controllers;
using Netch.Forms;
using Netch.Utils;
using Vanara.PInvoke;
using static Vanara.PInvoke.User32;
namespace Netch
{
public static class Netch
{
private static readonly Stopwatch Stopwatch = new();
public static void StartStopwatch(string name)
{
if (Stopwatch.IsRunning)
throw new Exception();
Stopwatch.Start();
Console.WriteLine($"Start {name} Stopwatch");
}
public static void TimePoint(string name, bool restart = true)
{
if (!Stopwatch.IsRunning)
throw new Exception();
Stopwatch.Stop();
Console.WriteLine($"{name} Stopwatch: {Stopwatch.ElapsedMilliseconds}");
if (restart)
Stopwatch.Restart();
}
/// <summary>
/// 应用程序的主入口点
/// </summary>
[STAThread]
public static void Main(string[] args)
{
#if DEBUG
AttachConsole();
#else
if (args.Contains("-console"))
if (!NativeMethods.AttachConsole(-1))
NativeMethods.AllocConsole();
AttachConsole();
#endif
StartStopwatch("Netch");
// 设置当前目录
Directory.SetCurrentDirectory(Global.NetchDir);
@@ -36,14 +66,15 @@ namespace Netch
if (!Directory.Exists(item))
Directory.CreateDirectory(item);
TimePoint("Clean Old, Create Directory");
// 加载配置
Configuration.Load();
TimePoint("Load Configuration");
// 检查是否已经运行
if (!Global.Mutex.WaitOne(0, false))
{
OnlyInstance.Send(OnlyInstance.Commands.Show);
Logging.Info("唤起单实例");
ShowOpened();
// 退出进程
Environment.Exit(1);
@@ -72,11 +103,8 @@ namespace Netch
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
Task.Run(() => { Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}"); });
Task.Run(() =>
{
Logging.Info("启动单实例");
OnlyInstance.Server();
});
TimePoint("Get Info, Pre-Form");
// 绑定错误捕获
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
@@ -87,10 +115,52 @@ namespace Netch
Application.Run(Global.MainForm);
}
private static void AttachConsole()
{
if (!NativeMethods.AttachConsole(-1))
NativeMethods.AllocConsole();
}
public static void Application_OnException(object sender, ThreadExceptionEventArgs e)
{
Logging.Error(e.Exception.ToString());
Utils.Utils.Open(Logging.LogFile);
}
private static void ShowOpened()
{
HWND GetWindowHandleByPidAndTitle(int process, string title)
{
var sb = new StringBuilder(256);
HWND pLast = IntPtr.Zero;
do
{
pLast = FindWindowEx(HWND.NULL, pLast, null, null);
GetWindowThreadProcessId(pLast, out var id);
if (id != process)
continue;
if (GetWindowText(pLast, sb, sb.Capacity) <= 0)
continue;
if (sb.ToString().Equals(title))
return pLast;
} while (pLast != IntPtr.Zero);
return HWND.NULL;
}
var self = Process.GetCurrentProcess();
var activeProcess = Process.GetProcessesByName("Netch").Single(p => p.Id != self.Id);
HWND handle = activeProcess.MainWindowHandle;
if (handle.IsNull)
handle = GetWindowHandleByPidAndTitle(activeProcess.Id, "Netch");
if (handle.IsNull)
return;
ShowWindow(handle, ShowWindowCommand.SW_NORMAL);
SwitchToThisWindow(handle, true);
}
}
}

View File

@@ -14,6 +14,9 @@
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<!-- <EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>-->
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -43,6 +46,10 @@
<PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.66" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Nullable.Extended.Analyzer" Version="1.2.4089">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" />
<PackageReference Include="System.Text.Json" Version="5.0.1" />
@@ -51,8 +58,7 @@
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.5" />
<PackageReference Include="WindowsFirewallHelper" Version="2.0.4.70-beta2" />
<PackageReference Include="WindowsJobAPI" Version="5.0.1" />
<PackageReference Include="WindowsProxy" Version="5.0.0" />
<PackageReference Include="WindowsProxy" Version="5.0.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">

View File

@@ -1,10 +1,10 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
@@ -13,13 +13,13 @@ namespace Netch.Properties {
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
@@ -33,7 +33,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
@@ -47,8 +47,8 @@ namespace Netch.Properties {
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
@@ -61,7 +61,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] _7za {
get {
@@ -71,17 +71,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] abp_js {
get {
object obj = ResourceManager.GetObject("abp_js", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap CopyLink {
get {
@@ -91,17 +81,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] defaultTUNTAP {
get {
object obj = ResourceManager.GetObject("defaultTUNTAP", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap delete {
get {
@@ -111,7 +91,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap edit {
get {
@@ -121,7 +101,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// 查找类似于 (图标) 的 System.Drawing.Icon 类型的本地化资源。
/// </summary>
internal static System.Drawing.Icon icon {
get {
@@ -131,7 +111,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap Netch {
get {
@@ -141,7 +121,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap speed {
get {
@@ -151,7 +131,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap Sponsor {
get {
@@ -161,7 +141,7 @@ namespace Netch.Properties {
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] zh_CN {
get {

View File

@@ -118,10 +118,6 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="defaultTUNTAP" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\defaultTUNTAP;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="zh_CN" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\zh-CN;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089</value>
@@ -150,10 +146,6 @@
<value>..\Resources\CopyLink.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="abp_js" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\abp.js.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="7za" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\7za.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089</value>

Binary file not shown.

View File

@@ -1,6 +0,0 @@
[Generic]
Address = 10.0.236.10
Netmask = 255.255.255.0
Gateway = 10.0.236.1
DNS = 1.1.1.1
UseCustomDNS = False

View File

@@ -165,6 +165,9 @@
"SS DLL": "SS DLL",
"Modify System DNS": "修改系统 DNS",
"Proxy Protocol": "代理协议",
"DNS Redirector": "DNS转发",
"ICMP Redirector": "ICMP转发",
"Child Process Handle": "子进程代理",
"ProfileCount": "快捷配置数量",
"Delay test after start": "启动后延迟测试",
"ServerPingType": "测速方式",

View File

@@ -1,4 +1,5 @@
using Netch.Forms;
using Netch.Utils;
namespace Netch.Servers.Shadowsocks.Form
{
@@ -8,7 +9,7 @@ namespace Netch.Servers.Shadowsocks.Form
{
server ??= new Shadowsocks();
Server = server;
CreateTextBox("Password", "Password", s => true, s => server.Password = s, server.Password);
CreateTextBox("Password", "Password", s => !s.IsNullOrWhiteSpace(), s => server.Password = s, server.Password);
CreateComboBox("EncryptMethod", "Encrypt Method", SSGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod);
CreateTextBox("Plugin", "Plugin", s => true, s => server.Plugin = s, server.Plugin);
CreateTextBox("PluginsOption", "Plugin Options", s => true, s => server.PluginOption = s, server.PluginOption);

View File

@@ -1,17 +1,13 @@
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Netch.Controllers;
using Netch.Models;
using Netch.Utils;
namespace Netch.Servers.Shadowsocks
{
public class SSController : Guard, IServerController
{
public bool DllFlag;
public override string MainFile { get; protected set; } = "Shadowsocks.exe";
protected override IEnumerable<string> StartedKeywords { get; } = new[] {"listening at"};
@@ -28,70 +24,60 @@ namespace Netch.Servers.Shadowsocks
{
var server = (Shadowsocks) s;
DllFlag = Global.Settings.BootShadowsocksFromDLL && mode.Type is 0 or 1 or 2 && !server.HasPlugin();
//从DLL启动Shaowsocks
if (DllFlag)
var command = new SSParameter
{
State = State.Starting;
var client = Encoding.UTF8.GetBytes($"{this.LocalAddress()}:{this.Socks5LocalPort()}");
var remote = Encoding.UTF8.GetBytes($"{server.AutoResolveHostname()}:{server.Port}");
var passwd = Encoding.UTF8.GetBytes($"{server.Password}");
var method = Encoding.UTF8.GetBytes($"{server.EncryptMethod}");
if (!ShadowsocksDLL.Info(client, remote, passwd, method))
{
State = State.Stopped;
throw new MessageException("DLL SS INFO 设置失败!");
}
Logging.Info("DLL SS INFO 设置成功!");
if (!ShadowsocksDLL.Start())
{
State = State.Stopped;
throw new MessageException("DLL SS 启动失败!");
}
Logging.Info("DLL SS 启动成功!");
State = State.Started;
return;
}
#region Argument
var argument = new StringBuilder();
argument.Append($"-s {server.AutoResolveHostname()} " + $"-p {server.Port} " + $"-b {this.LocalAddress()} " +
$"-l {this.Socks5LocalPort()} " + $"-m {server.EncryptMethod} " + $"-k \"{server.Password}\" " + "-u");
if (!string.IsNullOrWhiteSpace(server.Plugin) && !string.IsNullOrWhiteSpace(server.PluginOption))
argument.Append($" --plugin {server.Plugin}" + $" --plugin-opts \"{server.PluginOption}\"");
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
};
if (mode.BypassChina)
argument.Append($" --acl \"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}\"");
command.acl = $"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}";
#endregion
StartInstanceAuto(command.ToString());
}
StartInstanceAuto(argument.ToString());
[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; }
}
public override void Stop()
{
if (DllFlag)
ShadowsocksDLL.Stop();
else
StopInstance();
}
private class ShadowsocksDLL
{
[DllImport("shadowsocks-windows-dynamic", CallingConvention = CallingConvention.Cdecl)]
public static extern bool Info(byte[] client, byte[] remote, byte[] passwd, byte[] method);
[DllImport("shadowsocks-windows-dynamic", CallingConvention = CallingConvention.Cdecl)]
public static extern bool Start();
[DllImport("shadowsocks-windows-dynamic", CallingConvention = CallingConvention.Cdecl)]
public static extern void Stop();
StopInstance();
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Netch.Servers.Shadowsocks
/// <summary>
/// 密码
/// </summary>
public string? Password { get; set; }
public string Password { get; set; } = string.Empty;
/// <summary>
/// 插件

View File

@@ -1,4 +1,5 @@
using Netch.Forms;
using Netch.Utils;
namespace Netch.Servers.ShadowsocksR.Form
{
@@ -8,7 +9,7 @@ namespace Netch.Servers.ShadowsocksR.Form
{
server ??= new ShadowsocksR();
Server = server;
CreateTextBox("Password", "Password", s => true, s => server.Password = s, server.Password);
CreateTextBox("Password", "Password", s => !s.IsNullOrWhiteSpace(), s => server.Password = s, server.Password);
CreateComboBox("EncryptMethod", "Encrypt Method", SSRGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod);
CreateComboBox("Protocol", "Protocol", SSRGlobal.Protocols, s => server.Protocol = s, server.Protocol);
CreateTextBox("ProtocolParam", "Protocol Param", s => true, s => server.ProtocolParam = s, server.ProtocolParam);

View File

@@ -24,31 +24,64 @@ namespace Netch.Servers.ShadowsocksR
{
var server = (ShadowsocksR) s;
#region Argument
var argument = new StringBuilder();
argument.Append($"-s {server.AutoResolveHostname()} -p {server.Port} -k \"{server.Password}\" -m {server.EncryptMethod} -t 120");
if (!string.IsNullOrEmpty(server.Protocol))
var command = new SSRParameter
{
argument.Append($" -O {server.Protocol}");
if (!string.IsNullOrEmpty(server.ProtocolParam))
argument.Append($" -G \"{server.ProtocolParam}\"");
}
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
};
if (!string.IsNullOrEmpty(server.OBFS))
{
argument.Append($" -o {server.OBFS}");
if (!string.IsNullOrEmpty(server.OBFSParam))
argument.Append($" -g \"{server.OBFSParam}\"");
}
argument.Append($" -b {this.LocalAddress()} -l {this.Socks5LocalPort()} -u");
if (mode.BypassChina)
argument.Append($" --acl \"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}\"");
command.acl = $"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}";
#endregion
StartInstanceAuto(command.ToString());
}
StartInstanceAuto(argument.ToString());
[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; }
}
public override void Stop()

View File

@@ -7,26 +7,16 @@ namespace Netch.Servers.ShadowsocksR
{
public override string Type { get; } = "SSR";
/// <summary>
/// 加密方式
/// </summary>
public string EncryptMethod { get; set; } = SSRGlobal.EncryptMethods[0];
/// <summary>
/// 混淆
/// </summary>
public string OBFS { get; set; } = SSRGlobal.OBFSs[0];
/// <summary>
/// 混淆参数
/// </summary>
public string? OBFSParam { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; } = string.Empty;
/// <summary>
/// 加密方式
/// </summary>
public string EncryptMethod { get; set; } = SSRGlobal.EncryptMethods[0];
/// <summary>
/// 协议
/// </summary>
@@ -36,6 +26,16 @@ namespace Netch.Servers.ShadowsocksR
/// 协议参数
/// </summary>
public string? ProtocolParam { get; set; }
/// <summary>
/// 混淆
/// </summary>
public string OBFS { get; set; } = SSRGlobal.OBFSs[0];
/// <summary>
/// 混淆参数
/// </summary>
public string? OBFSParam { get; set; }
}
public class SSRGlobal

View File

@@ -13,7 +13,7 @@
/// <summary>
/// 额外 ID
/// </summary>
public string aid { get; set; } = string.Empty;
public int aid { get; set; }
/// <summary>
/// 伪装域名HTTPWS
@@ -38,7 +38,7 @@
/// <summary>
/// 端口
/// </summary>
public string port { get; set; } = string.Empty;
public ushort port { get; set; }
/// <summary>
/// 备注
@@ -58,6 +58,6 @@
/// <summary>
/// 链接版本
/// </summary>
public string v { get; set; } = string.Empty;
public int v { get; set; } = 2;
}
}

View File

@@ -77,9 +77,7 @@ namespace Netch.Servers.V2ray
var parameter = new Dictionary<string, string>();
// protocol-specific fields
parameter.Add("type", server.TransferProtocol);
if (server.EncryptMethod == "none")
// VLESS outbounds[].settings.encryption当前可选值只有 none
parameter.Add("encryption", server.EncryptMethod);
parameter.Add("encryption", server.EncryptMethod);
// transport-specific fields
switch (server.TransferProtocol)

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 Netch.Controllers;
using Netch.Models;
using Netch.Servers.V2ray;
@@ -43,12 +44,12 @@ namespace Netch.Servers.VMess
var vmessJson = JsonSerializer.Serialize(new V2rayNSharing
{
v = "2",
v = 2,
ps = server.Remark,
add = server.Hostname,
port = server.Port.ToString(),
port = server.Port,
id = server.UserID,
aid = server.AlterID.ToString(),
aid = server.AlterID,
net = server.TransferProtocol,
type = server.FakeType,
host = server.Host,
@@ -85,13 +86,14 @@ namespace Netch.Servers.VMess
return V2rayUtils.ParseVUri(text);
}
V2rayNSharing vmess = JsonSerializer.Deserialize<V2rayNSharing>(s)!;
V2rayNSharing vmess = JsonSerializer.Deserialize<V2rayNSharing>(s,
new JsonSerializerOptions {NumberHandling = JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowReadingFromString})!;
data.Remark = vmess.ps;
data.Hostname = vmess.add;
data.Port = ushort.Parse(vmess.port);
data.Port = vmess.port;
data.UserID = vmess.id;
data.AlterID = int.Parse(vmess.aid);
data.AlterID = vmess.aid;
data.TransferProtocol = vmess.net;
data.FakeType = vmess.type;

View File

@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Netch.Controllers;
using Netch.Properties;
using Netch.Utils;
@@ -14,65 +14,140 @@ namespace Netch.Updater
{
public class Updater
{
private static IEnumerable<string> _keepDirectory = new List<string>(new[] {"data", "mode\\Custom"});
private readonly string _targetPath;
private readonly string _tempFolder;
private readonly string _updateFilePath;
#region Download Update and apply update
private Updater(string updateFilePath, string targetPath)
/// <summary>
/// Download Update and apply update (all arguments are FullPath)
/// </summary>
/// <param name="downloadDirectory"></param>
/// <param name="installDirectory"></param>
/// <param name="onDownloadProgressChanged"></param>
/// <param name="keyword"></param>
/// <exception cref="MessageException"></exception>
public static void DownloadAndUpdate(string downloadDirectory,
string installDirectory,
DownloadProgressChangedEventHandler onDownloadProgressChanged,
string? keyword = null)
{
_targetPath = targetPath;
_tempFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(_tempFolder);
UpdateChecker.GetLatestUpdateFileNameAndHash(out var updateFileName, out var sha256, keyword);
_updateFilePath = Path.GetFullPath(updateFilePath);
_keepDirectory = _keepDirectory.Select(s => Path.Combine(targetPath, s));
// update file Full Path
var updateFile = Path.Combine(downloadDirectory, updateFileName);
var updater = new Updater(updateFile, installDirectory);
if (File.Exists(updateFile))
{
if (Utils.Utils.SHA256CheckSum(updateFile) == sha256)
{
updater.ApplyUpdate();
return;
}
File.Delete(updateFile);
}
DownloadUpdateFile(onDownloadProgressChanged, updateFile, sha256);
updater.ApplyUpdate();
}
/// <summary>
/// Download Update File
/// </summary>
/// <param name="onDownloadProgressChanged"></param>
/// <param name="fileFullPath"></param>
/// <param name="sha256"></param>
/// <exception cref="MessageException"></exception>
private static void DownloadUpdateFile(DownloadProgressChangedEventHandler onDownloadProgressChanged, string fileFullPath, string sha256)
{
using WebClient client = new();
try
{
client.DownloadProgressChanged += onDownloadProgressChanged;
client.DownloadFile(new Uri(UpdateChecker.LatestRelease.assets[0].browser_download_url), fileFullPath);
}
finally
{
client.DownloadProgressChanged -= onDownloadProgressChanged;
}
if (Utils.Utils.SHA256CheckSum(fileFullPath) != sha256)
throw new MessageException(i18N.Translate("The downloaded file has the wrong hash"));
}
#endregion
private readonly string _updateFile;
private readonly string _installDirectory;
private readonly string _tempDirectory;
private Updater(string updateFile, string installDirectory)
{
_updateFile = updateFile;
_installDirectory = installDirectory;
_tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(_tempDirectory);
}
#region Apply Update
private static readonly ImmutableArray<string> KeepDirectories = new List<string> {"data", "mode\\Custom"}.ToImmutableArray();
private void ApplyUpdate()
{
var extractPath = Path.Combine(_tempFolder, "extract");
// extract Update file to {tempDirectory}\extract
var extractPath = Path.Combine(_tempDirectory, "extract");
int exitCode;
if ((exitCode = Extract(extractPath, true)) != 0)
throw new Exception(i18N.Translate($"7za exit with code {exitCode}"));
// rename install directory files with .old suffix unless in keep folders
MarkFilesOld();
MoveAllFilesOver(Path.Combine(extractPath, "Netch"), _targetPath);
// move {tempDirectory}\extract\Netch to install folder
MoveAllFilesOver(Path.Combine(extractPath, "Netch"), _installDirectory);
// save, release mutex, then exit
Configuration.Save();
Global.Mutex.ReleaseMutex();
Global.MainForm.Invoke(new Action(() => { Global.Mutex.ReleaseMutex(); }));
Process.Start(Global.NetchExecutable);
Global.MainForm.Exit(true, false);
}
private void MarkFilesOld()
{
foreach (var file in Directory.GetFiles(_targetPath, "*", SearchOption.AllDirectories))
// extend keepDirectories relative path to absolute path
var extendedKeepDirectories = KeepDirectories.Select(d => Path.Combine(_installDirectory, d)).ToImmutableArray();
// weed out keep files
List<string> filesToDelete = new();
foreach (var file in Directory.GetFiles(_installDirectory, "*", SearchOption.AllDirectories))
{
if (_keepDirectory.Any(p => file.StartsWith(p)))
if (extendedKeepDirectories.Any(p => file.StartsWith(p)))
continue;
try
{
File.Move(file, file + ".old");
}
catch
{
throw new Exception("Updater wasn't able to rename file: " + file);
}
if (Path.GetFileName(file) is ModeHelper.DISABLE_MODE_DIRECTORY_FILENAME)
continue;
filesToDelete.Add(file);
}
// rename files
foreach (var file in filesToDelete)
File.Move(file, file + ".old");
}
private int Extract(string destDirName, bool overwrite)
{
var temp7za = Path.Combine(_tempFolder, "7za.exe");
// release 7za.exe to {tempDirectory}\7za.exe
var temp7za = Path.Combine(_tempDirectory, "7za.exe");
if (!File.Exists(temp7za))
File.WriteAllBytes(temp7za, Resources._7za);
// run 7za
var argument = new StringBuilder();
argument.Append($" x \"{_updateFilePath}\" -o\"{destDirName}\"");
argument.Append($" x \"{_updateFile}\" -o\"{destDirName}\"");
if (overwrite)
argument.Append(" -y");
@@ -108,6 +183,10 @@ namespace Netch.Updater
}
}
#endregion
#region Clean files marked as old when start
public static void CleanOld(string targetPath)
{
foreach (var f in Directory.GetFiles(targetPath, "*.old", SearchOption.AllDirectories))
@@ -121,40 +200,6 @@ namespace Netch.Updater
}
}
public static async Task DownloadAndUpdate(string downloadPath,
string targetPath,
DownloadProgressChangedEventHandler onDownloadProgressChanged)
{
var keyword = (string?) null;
if (!UpdateChecker.GetFileNameAndHashFromMarkdownForm(UpdateChecker.LatestRelease.body, out var fileName, out var sha256, keyword))
throw new Exception(i18N.Translate("parse release note failed"));
var fileFullPath = Path.Combine(downloadPath, fileName);
var updater = new Updater(fileFullPath, targetPath);
if (!(File.Exists(fileFullPath) && Utils.Utils.SHA256CheckSum(fileFullPath) == sha256))
{
using WebClient client = new();
try
{
client.DownloadProgressChanged += onDownloadProgressChanged;
await client.DownloadFileTaskAsync(new Uri(UpdateChecker.LatestRelease.assets[0].browser_download_url), fileFullPath);
}
catch (Exception e)
{
throw new Exception(i18N.Translate("Download Update Failed", ": ") + e.Message);
}
finally
{
client.DownloadProgressChanged -= onDownloadProgressChanged;
}
if (Utils.Utils.SHA256CheckSum(fileFullPath) != sha256)
throw new Exception(i18N.Translate("The downloaded file has the wrong hash"));
}
updater.ApplyUpdate();
}
#endregion
}
}

View File

@@ -57,9 +57,6 @@ namespace Netch.Utils
{
case null:
break;
case SSController {DllFlag: true}:
instances.Add(Process.GetCurrentProcess());
break;
case Guard instanceController:
if (instanceController.Instance != null)
instances.Add(instanceController.Instance);

View File

@@ -33,43 +33,37 @@ namespace Netch.Utils
{
if (File.Exists(SETTINGS_JSON))
{
Global.Settings = ParseSetting(File.ReadAllText(SETTINGS_JSON));
try
{
using var fileStream = File.OpenRead(SETTINGS_JSON);
var settings = JsonSerializer.DeserializeAsync<Setting>(fileStream, JsonSerializerOptions).Result!;
CheckSetting(settings);
Global.Settings = settings;
}
catch (Exception e)
{
Logging.Error(e.ToString());
Utils.Open(Logging.LogFile);
Environment.Exit(-1);
Global.Settings = null!;
}
}
else
{
// 弹出提示
i18N.Load("System");
// 创建 data 文件夹并保存默认设置
// 保存默认设置
Save();
}
}
public static Setting ParseSetting(string text)
private static void CheckSetting(Setting settings)
{
try
{
var settings = JsonSerializer.Deserialize<Setting>(text, JsonSerializerOptions)!;
settings.Profiles.RemoveAll(p => p.ServerRemark == string.Empty || p.ModeRemark == string.Empty);
#region Check Profile
settings.Profiles.RemoveAll(p => p.ServerRemark == string.Empty || p.ModeRemark == string.Empty);
if (settings.Profiles.Any(p => settings.Profiles.Any(p1 => p1 != p && p1.Index == p.Index)))
for (var i = 0; i < settings.Profiles.Count; i++)
settings.Profiles[i].Index = i;
#endregion
return settings;
}
catch (Exception e)
{
Logging.Error(e.ToString());
Utils.Open(Logging.LogFile);
Environment.Exit(-1);
return null!;
}
if (settings.Profiles.Any(p => settings.Profiles.Any(p1 => p1 != p && p1.Index == p.Index)))
for (var i = 0; i < settings.Profiles.Count; i++)
settings.Profiles[i].Index = i;
}
/// <summary>

View File

@@ -14,15 +14,15 @@ namespace Netch.Utils.HttpProxyHandler
{
private static HttpWebServer? _httpWebServer;
private static string? _pacContent;
public static readonly string PacPrefix= $"http://127.0.0.1:{Global.Settings.Pac_Port}/pac/";
public static string InitPACServer(string address)
public static string InitPACServer()
{
try
{
_pacContent = GetPacList(address);
var prefixes = $"http://{address}:{Global.Settings.Pac_Port}/pac/";
_pacContent = GetPacList("127.0.0.1");
_httpWebServer = new HttpWebServer(SendResponse, prefixes);
_httpWebServer = new HttpWebServer(SendResponse, PacPrefix);
Task.Run(() => _httpWebServer.StartWaitingRequest());
var pacUrl = GetPacUrl();
@@ -77,7 +77,7 @@ namespace Netch.Utils.HttpProxyHandler
/// <returns></returns>
public static string GetPacUrl()
{
return $"http://127.0.0.1:{Global.Settings.Pac_Port}/pac/?t={DateTime.Now:yyyyMMddHHmmssfff}";
return PacPrefix + $"?t={DateTime.Now:yyyyMMddHHmmssfff}";
}
}
}

View File

@@ -40,14 +40,23 @@ namespace Netch.Utils
private static void Write(string text, LogLevel logLevel)
{
var contents = $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Global.EOF}";
if (Global.Testing)
#if DEBUG
switch (logLevel)
{
Console.WriteLine(contents);
return;
case LogLevel.INFO:
case LogLevel.WARNING:
Console.Write(contents);
break;
case LogLevel.ERROR:
Console.Error.Write(contents);
break;
default:
throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null);
}
#else
lock (FileLock)
File.AppendAllText(LogFile, contents);
#endif
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Netch.Controllers;
@@ -12,9 +11,33 @@ namespace Netch.Utils
public static class ModeHelper
{
private const string MODE_DIR = "mode";
public const string DISABLE_MODE_DIRECTORY_FILENAME = "disabled";
public static readonly string ModeDirectory = Path.Combine(Global.NetchDir, $"{MODE_DIR}\\");
private static readonly FileSystemWatcher FileSystemWatcher;
static ModeHelper()
{
FileSystemWatcher = new FileSystemWatcher(ModeDirectory)
{
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.FileName,
IncludeSubdirectories = true,
EnableRaisingEvents = true
};
FileSystemWatcher.Changed += OnModeChanged;
FileSystemWatcher.Created += OnModeChanged;
FileSystemWatcher.Deleted += OnModeChanged;
FileSystemWatcher.Renamed += OnModeChanged;
}
private static void OnModeChanged(object sender, FileSystemEventArgs e)
{
Load();
Global.MainForm.LoadModes();
}
public static string GetRelativePath(string fullName)
{
return fullName.Substring(ModeDirectory.Length);
@@ -25,84 +48,42 @@ namespace Netch.Utils
return Path.Combine(ModeDirectory, relativeName);
}
public static string GetFullPath(Mode mode)
{
return Path.Combine(ModeDirectory, mode.RelativePath);
}
/// <summary>
/// 从模式文件夹读取模式并为 <see cref="Forms.MainForm.ModeComboBox" /> 绑定数据
/// 从模式文件夹读取模式
/// </summary>
public static void Load()
{
Global.Modes.Clear();
if (!Directory.Exists(MODE_DIR))
return;
var stack = new Stack<string>();
stack.Push(MODE_DIR);
while (stack.Count > 0)
{
var dirInfo = new DirectoryInfo(stack.Pop());
try
{
foreach (var childDirInfo in dirInfo.GetDirectories())
stack.Push(childDirInfo.FullName);
foreach (var childFileInfo in dirInfo.GetFiles().Where(info => info.Name.EndsWith(".txt")))
LoadModeFile(childFileInfo.FullName);
}
catch
{
// ignored
}
}
LoadModeDirectory(ModeDirectory);
Sort();
}
private static void LoadModeFile(string fullName)
private static void LoadModeDirectory(string modeDirectory)
{
var mode = new Mode(fullName);
var content = File.ReadAllLines(fullName);
if (content.Length == 0)
return;
for (var i = 0; i < content.Length; i++)
try
{
var text = content[i].Trim();
foreach (var directory in Directory.GetDirectories(modeDirectory))
LoadModeDirectory(directory);
if (i == 0)
{
if (text.First() != '#')
return;
// skip Directory with a disabled file in
if (File.Exists(Path.Combine(modeDirectory, DISABLE_MODE_DIRECTORY_FILENAME)))
return;
foreach (var file in Directory.GetFiles(modeDirectory).Where(f => f.EndsWith(".txt")))
try
{
var splited = text.Substring(1).SplitTrimEntries(',');
mode.Remark = splited[0];
var typeResult = int.TryParse(splited.ElementAtOrDefault(1), out var type);
mode.Type = typeResult ? type : 0;
var bypassChinaResult = int.TryParse(splited.ElementAtOrDefault(2), out var bypassChina);
mode.BypassChina = mode.ClientRouting() && bypassChinaResult && bypassChina == 1;
Global.Modes.Add(new Mode(file));
}
catch
catch (Exception)
{
return;
// ignored
}
}
else
{
mode.Rule.Add(text);
}
}
Global.Modes.Add(mode);
catch
{
// ignored
}
}
private static void Sort()
@@ -110,21 +91,12 @@ namespace Netch.Utils
Global.Modes.Sort((a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
}
public static void Add(Mode mode)
{
Global.Modes.Add(mode);
Sort();
Global.MainForm.LoadModes();
}
public static void Delete(Mode mode)
{
var fullName = GetFullPath(mode);
if (File.Exists(fullName))
File.Delete(fullName);
if (mode.FullName == null)
throw new ArgumentException(nameof(mode.FullName));
Global.Modes.Remove(mode);
Global.MainForm.LoadModes();
File.Delete(mode.FullName);
}
public static bool SkipServerController(Server server, Mode mode)
@@ -141,17 +113,15 @@ namespace Netch.Utils
};
}
public static IModeController? GetModeControllerByType(int type, out ushort? port, out string portName, out PortType portType)
public static IModeController? GetModeControllerByType(int type, out ushort? port, out string portName)
{
port = null;
portName = string.Empty;
portType = PortType.Both;
switch (type)
{
case 0:
port = Global.Settings.RedirectorTCPPort;
portName = "Redirector TCP";
portType = PortType.TCP;
return new NFController();
case 1:
case 2:
@@ -160,7 +130,6 @@ namespace Netch.Utils
case 5:
port = Global.Settings.HTTPLocalPort;
portName = "HTTP";
portType = PortType.TCP;
StatusPortInfoText.HttpPort = (ushort) port;
return new HTTPController();
case 4:

View File

@@ -1,78 +0,0 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Netch.Utils
{
public static class OnlyInstance
{
public enum Commands
{
Show,
Exit
}
public static event EventHandler<Commands>? Called;
private static void OnCalled(Commands e)
{
Called?.Invoke(null, e);
}
public static async void Server()
{
try
{
const int tryLimit = 3;
var i = tryLimit;
while (i > 0)
try
{
PortHelper.CheckPort(Global.Settings.UDPSocketPort, PortType.UDP);
if (i != tryLimit)
Configuration.Save();
break;
}
catch
{
Global.Settings.UDPSocketPort = PortHelper.GetAvailablePort(PortType.UDP);
i--;
}
var data = new byte[1024];
var newsock = new UdpClient(new IPEndPoint(IPAddress.Loopback, Global.Settings.UDPSocketPort));
while (true)
{
var result = await newsock.ReceiveAsync();
data = result.Buffer;
if (Enum.TryParse<Commands>(Encoding.ASCII.GetString(data, 0, data.Length), out var command))
OnCalled(command);
}
}
catch (Exception e)
{
Logging.Error(e.ToString());
}
}
public static async void Send(Commands command)
{
try
{
using var udpClient = new UdpClient(new IPEndPoint(IPAddress.Loopback, Global.Settings.UDPSocketPort));
udpClient.Connect(IPAddress.Loopback, Global.Settings.UDPSocketPort);
var sendBytes = Encoding.ASCII.GetBytes(command.ToString());
await udpClient.SendAsync(sendBytes, sendBytes.Length);
udpClient.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}

View File

@@ -4,6 +4,8 @@ using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using Netch.Models;
using static Vanara.PInvoke.IpHlpApi;
using static Vanara.PInvoke.Ws2_32;
namespace Netch.Utils
{
@@ -26,6 +28,18 @@ namespace Netch.Utils
}
}
public static Process? GetProcessByUsedTcpPort(ushort port)
{
if (port == 0)
throw new ArgumentOutOfRangeException();
var row = GetTcpTable2().SingleOrDefault(r => ntohs((ushort) r.dwLocalPort) == port);
if (row.dwOwningPid == 0)
return null;
return Process.GetProcessById((int) row.dwOwningPid);
}
private static void GetReservedPortRange(PortType portType, ref List<Range> targetList)
{
var process = new Process

View File

@@ -20,6 +20,7 @@ namespace Netch.Utils
}
catch
{
// Unsupported Server Type
return JsonSerializer.Deserialize<Server>(jsonElement.GetRawText(), new JsonSerializerOptions())!;
}
}

View File

@@ -48,8 +48,9 @@ namespace Netch.Utils
var sb = new StringBuilder();
foreach (var t in value)
{
if (new[] {'\\', '(', ')', '[', ']', '.'}.Any(s => s == t))
sb.Append(@"\");
var escapeCharacters = new[] {'\\', '*', '+', '?', '|', '{', '}', '[', ']', '(', ')', '^', '$', '.'};
if (escapeCharacters.Any(s => s == t))
sb.Append('\\');
sb.Append(t);
}

View File

@@ -21,8 +21,6 @@ namespace Netch.Utils
{
public static bool Open(string path)
{
if (Global.Testing)
return true;
try
{
Process.Start(new ProcessStartInfo
@@ -116,30 +114,11 @@ namespace Netch.Utils
}
}
public static void KillProcessByName(string name)
{
try
{
foreach (var p in Process.GetProcessesByName(name))
if (p.MainModule != null && p.MainModule.FileName.StartsWith(Global.NetchDir))
p.Kill();
}
catch (Win32Exception e)
{
Logging.Error($"结束进程 {name} 错误:" + e.Message);
}
catch (Exception)
{
// ignored
}
}
public static string GetFileVersion(string file)
{
return File.Exists(file) ? FileVersionInfo.GetVersionInfo(file).FileVersion : string.Empty;
}
public static void DrawCenterComboBox(object sender, DrawItemEventArgs e)
{
if (sender is ComboBox cbx)

View File

@@ -7,9 +7,9 @@
[![Netch CI](https://github.com/NetchX/Netch/workflows/Netch%20CI/badge.svg)](https://github.com/NetchX/Netch/actions)
[![License](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)
[中文说明](/docs/README.zh-CN.md)
[文档网站](https://netch.org/) [常见问题](https://netch.org/#/docs/zh-CN/faq)
[常见问题](https://netch.org/#/docs/zh-CN/faq)
[中文说明](README.zh-CN.md)
Game network accelerator
@@ -31,12 +31,12 @@ Currently supports the following protocols
- ShadowsocksR
- Trojan
- VMess
- VLess
- VLESS
As well, Netch avoid the restricted NAT problem caused by SSTap. You can use an NATTypeTester to test out what your NAT type is. When using SSTap to speed up some P2P gaming connections or the game is required for that kind of open NAT type, you may experience some bad situations such as unable to join the game
## Sponsor
<a href="https://www.jetbrains.com/?from=Netch"><img src=".github/jetbrains-variant-4.svg" alt="JetBrains" width="200"/></a>
<a href="https://www.jetbrains.com/?from=Netch"><img src="images/jetbrains-variant-4.svg" alt="JetBrains" width="200"/></a>
- [RabbitHosts](https://rabbithosts.com/cart.php)
- [ManSora](https://www.mansora.co/cart.php)
@@ -46,7 +46,7 @@ As well, Netch avoid the restricted NAT problem caused by SSTap. You can use an
- XMR *48ju3ELNZEa6wwPBMexCJ9G218BGY2XwhH6B6bmkFuJ3QgM4hPw2Pra35jPtuBZSc7SLNWeBpiWJZWjQeMAiLnTx2tH2Efx*
## Screenshots
![](docs/screenshots/main.png)
![](images/screenshots/main.png)
## Requirements
- [.NET Framework 4.8 (Included in Windows 10 1903+)](https://dotnet.microsoft.com/download/dotnet-framework/net48)

View File

@@ -7,9 +7,9 @@
[![Netch CI](https://github.com/NetchX/Netch/workflows/Netch%20CI/badge.svg)](https://github.com/NetchX/Netch/actions)
[![License](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)
游戏加速工具
[文档网站](https://netch.org/) [常见问题](https://netch.org/#/docs/zh-CN/faq)
[网站](https://netch.org/)
游戏加速工具
## TOC
- [Netch](#Netch)
@@ -17,8 +17,6 @@
- [简介](#简介)
- [赞助商](#赞助商)
- [捐赠](#捐赠)
- [新手入门](Quickstart.zh-CN.md)
- [进阶用法](Advanced_Usage.zh-CN.md)
- [依赖](#依赖)
- [语言支持](#语言支持)
@@ -31,15 +29,12 @@ Netch 是一款 Windows 平台的开源游戏加速工具Netch 可以实现
- ShadowsocksR
- Trojan
- VMess
- VLess
- VLESS
与此同时 Netch 避免了 SSTap 的 NAT 问题 ,检查 NAT 类型即可知道是否有 NAT 问题。使用 SSTap 加速部分 P2P 联机,对 NAT 类型有要求的游戏时,可能会因为 NAT 类型严格遇到无法加入联机,或者其他影响游戏体验的情况
需要更多特性请移步魔改仓库 [Netch-ForOwnUse](https://github.com/AmazingDM/Netch-ForOwnUse)
## 赞助商
<a href="https://www.jetbrains.com/?from=Netch"><img src="../.github/jetbrains-variant-4.svg" alt="JetBrains" width="200"/></a>
<a href="https://www.jetbrains.com/?from=Netch"><img src="images/jetbrains-variant-4.svg" alt="JetBrains" width="200"/></a>
- [RabbitHosts](https://rabbithosts.com/cart.php)
- [ManSora](https://www.mansora.co/cart.php)
@@ -48,16 +43,13 @@ Netch 是一款 Windows 平台的开源游戏加速工具Netch 可以实现
## 捐赠
- XMR *48ju3ELNZEa6wwPBMexCJ9G218BGY2XwhH6B6bmkFuJ3QgM4hPw2Pra35jPtuBZSc7SLNWeBpiWJZWjQeMAiLnTx2tH2Efx*
## 新手入门
[新手入门教程](Quickstart.zh-CN.md)
## 进阶用法
[进阶教程](Advanced_Usage.zh-CN.md)
## 依赖
- [.NET Framework 4.8 (Windows 10 1903+ 已包含)](https://dotnet.microsoft.com/download/dotnet-framework/thank-you/net48-offline-installer)
## 语言支持
Netch 支持多种语言,在启动时会根据系统语言选择自身语言。如果需要手动切换语言,可以在启动时加入命令行参数,命令行参数为目前支持的语言代码,可以去 [NetchTranslation/i18n](https://github.com/NetchX/NetchTranslation/tree/master/i18n) 文件夹下查看外部支持的语言代码文件。Netch 目前内置 en-USzh-CN外置 zh-TW。欢迎大家为 [NetchTranslation](https://github.com/NetchX/NetchTranslation) 提供其他语言的翻译
Netch 内置 en-USzh-CN外置 zh-TW 等,默认根据系统语言选择语言。
[Netch 外置语言仓库](https://github.com/NetchX/NetchTranslation) ,欢迎提供其他语言的翻译
## 引用
- [core](https://github.com/aiocloud/core)

View File

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

3
UnitTest/.gitignore vendored
View File

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

View File

@@ -6,10 +6,10 @@ using Netch.Utils;
namespace UnitTest
{
[TestClass]
public class FunctionTest : TestBase
public class Function : TestBase
{
[TestMethod]
public void TestLoadI18N()
public void LoadLanguage()
{
void TestLoad(string t)
{

98
UnitTest/ParameterTest.cs Normal file
View File

@@ -0,0 +1,98 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Netch.Models;
namespace UnitTest
{
[TestClass]
public class ParameterTest
{
[Verb]
private class VerbAndRealName : ParameterBase
{
[RealName("v")]
public string v1 { get; } = "a";
[RealName("v")]
public string v2 { get; } = "b";
}
private class Full : ParameterBase
{
[RealName("f")]
public string f1 { get; } = "a";
[RealName("f")]
public string f2 { get; } = "b";
}
private class FullWithVerb : ParameterBase
{
public string f { get; } = "a";
[Verb]
public string v { get; } = "b";
}
[Verb]
private class VerbWithFull : ParameterBase
{
public string v { get; } = "a";
[Full]
public string f { get; } = "b";
}
private class QuoteValue : ParameterBase
{
public static string pathValue = @"C:\Programe Files\Damn thats space";
[Quote]
public string path { get; set; } = pathValue;
}
private class FlagAndOptional : ParameterBase
{
public bool a { get; set; } = true;
public bool b { get; set; } = false;
[Optional]
public string c { get; set; } = string.Empty;
[Optional]
public string? d { get; set; } = null;
}
private class RequiredEmpty : ParameterBase
{
public string? udp { get; set; } = string.Empty;
}
private class RequiredNull : ParameterBase
{
public string? udp { get; set; } = null;
}
private class Number : ParameterBase
{
public ushort a { get; set; } = 1;
public int b { get; set; } = 1;
}
[TestMethod]
public void Test()
{
Assert.AreEqual(new VerbAndRealName().ToString(), "-v a -v b");
Assert.AreEqual(new Full().ToString(), "--f a --f b");
Assert.AreEqual(new FullWithVerb().ToString(), "--f a -v b");
Assert.AreEqual(new VerbWithFull().ToString(), "-v a --f b");
Assert.AreEqual(new QuoteValue().ToString(), $"--path \"{QuoteValue.pathValue}\"");
Assert.AreEqual(new FlagAndOptional().ToString(), "--a");
Assert.ThrowsException<RequiredArgumentValueInvalidException>(() => { _ = new RequiredEmpty().ToString(); });
Assert.ThrowsException<RequiredArgumentValueInvalidException>(() => { _ = new RequiredNull().ToString(); });
Assert.AreEqual(new Number().ToString(), "--a 1 --b 1");
}
}
}

View File

@@ -10,10 +10,10 @@ using Netch.Utils;
namespace UnitTest
{
[TestClass]
public class TestParseShareLink : TestBase
public class ParseShareLink : TestBase
{
[TestMethod]
public void TestServerFromSSR()
public void ParseSSR()
{
const string normalCase =
"ssr://MTI3LjAuMC4xOjEyMzQ6YXV0aF9hZXMxMjhfbWQ1OmFlcy0xMjgtY2ZiOnRsczEuMl90aWNrZXRfYXV0aDpZV0ZoWW1KaS8_b2Jmc3BhcmFtPVluSmxZV3QzWVRFeExtMXZaUQ";

View File

@@ -1,14 +1,6 @@
using Netch;
namespace UnitTest
namespace UnitTest
{
public class TestBase
{
protected TestBase()
{
#if DEBUG
Global.Testing = true;
#endif
}
}
}

View File

@@ -6,6 +6,7 @@
<UseWindowsForms>true</UseWindowsForms>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
@@ -23,8 +24,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.1" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.3" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.3" />
<PackageReference Include="System.Text.Json" Version="5.0.1" />
</ItemGroup>

View File

@@ -1,213 +0,0 @@
# Netch 模式
## 目录
1. [模式介绍](#模式介绍)
- 1.1 [模式 1 进程代理模式](#模式-1-进程代理模式)
- 1.2 [模式 2需要自己新建模式文件 TUN/TAP IP 黑名单代理模式](#模式-2需要自己新建模式文件-tuntap-ip-黑名单代理模式)
- 1.3 [模式 3 TUN/TAP IP 白名单)全局代理模式](#模式-3-tuntap-ip-白名单全局代理模式)
- 1.4 [模式 4 HTTP 系统代理](#模式-4-http-系统代理)
- 1.5 [模式 5 本地 Socks5 代理](#模式-5-本地-socks5-代理)
- 1.6 [模式 6 本地 Socks5 和 HTTP 代理](#模式-6-本地-socks5-和-http-代理)
2. [Socks 5 代理中转](#socks-5-代理中转)
3. [新建进程代理模式](#新建进程代理模式)
- 3.1 [模式](#模式)
- 3.2 [扫描](#扫描)
- 3.3 [启动](#启动)
## 模式介绍
目前 Netch 所有模式文件都在 `mode` 文件夹下。模式号即模式菜单中最左边中括号内数字
内置的模式中,如果模式名中有 Bypass China 的部分,即该模式会绕过中国 IP 段
模式 1 和模式 2 里面除了第一行格式不同,其他内容和 [SSTap-Rule](https://github.com/FQrabbit/SSTap-Rule) 相同。是否绕过中国的功能依赖于 [CNIP 文件](https://github.com/NetchX/Netch/blob/master/Netch/Resources/CNIP)
模式 3 到模式 5 的是否绕过中国的功能依赖于 [acl 文件](https://github.com/NetchX/Netch/blob/master/binaries/default.acl)
第一行格式均为如下样式,不同模式之间第一行的具体区别可以参照后面的内容
```Python
# 备注, 类型(是主项目 USAGE.zh-CN.md 里提到的模式类型的值减一), 是否绕过中国1 为是, 0 为否)
```
### 模式 1 进程代理模式
- 根据进程名进行代理
- 底层依赖于 [NetFilter SDK](https://netfiltersdk.com)
- 对于第一次使用 Netch 的用户而言,不需要做多余的事情
- 若 [NetFilter SDK](https://netfiltersdk.com) 的驱动不存在,会自动安装
- 若驱动版本过低,会自动更新
范例文件
在这个模式里第一行只有备注是有用的规则内容支持C++正则表达式
```
# 备注
进程名 1会被代理
!进程名 2不会被代理
csgo.exe
\\steam\\代理运行路径包含steam的所有程序
...
```
### 模式 2需要自己新建模式文件 TUN/TAP IP 黑名单代理模式
- 黑名单代理指的是,除了名单内的 IP 走代理,其他连接都不走代理
- 需要自己新建模式文件,第一行写法同模式 3只是需要把 2 改成 1
- 后续内容的格式同 [SSTap-rules](https://github.com/FQrabbit/SSTap-Rule),任何规则问题建议到那边去提
- 可以通过左下角的`设置`来配置 IP 地址子网掩码网关DNS
- 该模式下直连 IP 段无效,暂时没有代码实现
- 底层依赖于 [Tap-Windows](https://github.com/OpenVPN/tap-windows) 适配器等
- 如果 Netch 提示没有该适配器,可以直接安装 [Tap-Windows](https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe) 或者通过安装 [OpenVPN](https://openvpn.net/community-downloads/)[SSTap](https://github.com/mayunbaba2/SSTap-beta-setup) 的方式获得该适配器
范例文件
在这个模式里,是否绕过中国的值是无效的
```
# 备注, 1
无类别域间路由写法 1目的 IP 在这个子网内的网络请求都会被代理)
无类别域间路由写法 2
...
```
### 模式 3 TUN/TAP IP 白名单)全局代理模式
- 白名单代理指的是,除了名单内的 IP 不走代理,其他连接都走代理
- 可以通过左下角的`设置`来配置 IP 地址子网掩码网关DNS直连 IP 段
- 底层依赖于 [Tap-Windows](https://github.com/OpenVPN/tap-windows) 适配器tun2socks 等
- 如果 Netch 提示没有该适配器,可以直接安装 [Tap-Windows](https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe) 或者通过安装 [OpenVPN](https://openvpn.net/community-downloads/)[SSTap](https://github.com/mayunbaba2/SSTap-beta-setup) 的方式获得该适配器
范例文件
```
# 备注, 2, 是否绕过中国1 为是, 0 为否)
无类别域间路由写法 1目的 IP 只有在这个子网内的网络请求不会被代理,其他的都会被代理)
无类别域间路由写法 2
...
```
### 模式 4 HTTP 系统代理
- 默认地址和端口为 127.0.0.1:2802
- 端口可以在左下角设置里面更改
- 会被设置为系统代理
范例文件
```
# 备注, 3, 是否绕过中国1 为是, 0 为否)
(目前只有第一行是有效的)
```
### 模式 5 本地 Socks5 代理
- 默认地址和端口为 127.0.0.1:2801
- 端口可以在左下角设置里面更改
- 不会被设置为系统代理,对于 Chrome 之类使用系统代理的浏览器需要设置使用插件 SwitchyOmega 之后才能被正常代理
- 注意如果是使用 Firefox 的网络设置,请仅设置 Socks5 代理,清除其他代理配置,并取消勾选`为所有协议使用相同的代理服务器`
- 其他模式均含 Socks5 代理,本模式可以理解为仅 Socks5 代理\
范例文件
```
# 备注, 4, 是否绕过中国1 为是, 0 为否)
(目前只有第一行是有效的)
```
### 模式 6 本地 Socks5 和 HTTP 代理
- Socks5 代理的默认地址和端口为 127.0.0.1:2801
- HTTP 代理的默认地址和端口为 127.0.0.1:2802
- 端口可以在左下角设置里面更改
- 不会被设置为系统代理
范例文件
```
# 备注, 5, 是否绕过中国1 为是, 0 为否)
(目前只有第一行是有效的)
```
## Socks 5 代理中转
说明一下Netch 并非是以网页代理为目的开发的程序,如果需要网络代理为目的的程序,需要 PAC规则分流订阅管理等功能的请尽量参考使用以下软件而非 Netch均为 Windows 平台)
ShadowsocksR
- [HMBSbige/ShadowsocksR-Windows](https://github.com/HMBSbige/ShadowsocksR-Windows/releases)
Shadowsocks
- [Clash for Windows](https://github.com/Fndroid/clash_for_windows_pkg/releases)
V2Ray
- [V2RayN](https://github.com/2dust/v2rayN/releases)
如果你想使用的代理工具目前 Netch 还不支持,或者需要一些 Netch 目前没有的功能,如 V2Ray 自定义配置Socks5 本地代理规则分流的,可以在 Netch 里添加对应工具的本地 Socks5 代理端口后使用,注意如果你用的是模式 3 TUN/TAP IP 白名单)全局代理模式,记得在`设置 - 全局直连 IP`中添加你的服务器 IP 地址,否则会产生代理回环
## 新建进程代理模式
- 现在软件还处在早期开发阶段,可能后续版本会发生很大变化,操作仅供参考
当前版本已添加配置编辑功能,根据自己的情况,使用订阅或者别的方法添加代理配置,我这里使用的是剪贴板导入
![剪贴板导入](/docs/screenshots/advanced/importServer.png)
如果你发现你的程序没我截图的看起来清晰,可以右键 `Netch.exe - 属性 - 兼容性 - 更改高 DPI 设置 - 替代高 DPI 缩放执行 - 系统(增强)`
### 模式
如果你的游戏的模式已经被收录,也可以考虑在模式菜单中,选择使用已收录的模式。所有模式的文件,都在 `./mode/` 文件夹下,如果你需要多个模式的合并文件,可以使用记事本将其打开,将多个文件合并
ping 的值未必准确,因为这只是你本地到代理服务器而非游戏服务器的延迟
如果你的游戏的模式没被收录,可以看接下来的扫描步骤来手动创建模式
接着点击菜单栏上的`模式 - 创建进程模式`
![模式 - 创建进程模式](/docs/screenshots/advanced/createMode.png)
### 扫描
在弹出的窗口中点击`扫描`
![扫描](/docs/screenshots/advanced/modeForm.png)
选择你要加速的游戏的安装路径,根据游戏不同,可能需要选择多个不同的目录进行扫描,参见[萌鹰的 Netch 教程](https://www.eaglemoe.com/archives/142)(包括 GTAOL 和 R6S 的配置方法)
>4. 选定 GTA5 游戏目录,点击确定,软件会自动扫描目录下的 exe 程式并填写进去
>5. 再次点击扫描,选择 SocialClub 的安装地址(一般为 C:\Program Files\Rockstar Games\Social Club点击确定点击保存
>
>注意:加入游戏时请不要忘记加入社交组件,比如说 GTA 不要忘记 SocialClub ,彩虹六号不要忘记 Uplay如果游戏进程名与其他进程名重复则可手动修改已创建好的模式文件在进程名前加上绝对路径即可。csgo.exe -> C:\steam\game\Counter-Strike Global Offensive\csgo.exe
这里以CSGO为例只需添加CSGO游戏根目录即可
![选择路径](/docs/screenshots/advanced/scan.png)
扫描时可能需要稍等片刻,扫描后记得填写备注
如果需要添加单个程序,也可以在添加按钮左侧的编辑栏中手动输入并添加
之后点保存进行`保存`
![保存](/docs/screenshots/advanced/saveMode.png)
### 启动
最后确认服务器一栏和模式一栏均为之前自己添加并需要使用的,没问题后点击`启动`即可
![启动](/docs/screenshots/advanced/started.png)
启动后,你再去游戏根目录或者别的启动器如 SteamUplay 启动游戏即可。此时游戏就已经被代理了
如果在 Netch 启动前就启动了游戏,建议重启游戏
如果需要 SteamUplay 等启动器也被代理,参照前面的方式对 SteamUplay 根目录也进行扫描即可
## 语言支持
Netch 支持多种语言,在启动时会根据系统语言选择自身语言。如果需要手动切换语言,可以在启动时加入命令行参数,命令行参数为目前支持的语言代码,可以去 [NetchTranslation/i18n](https://github.com/NetchX/NetchTranslation/tree/master/i18n) 文件夹下查看外部支持的语言代码文件。Netch 目前内置 en-USzh-CN外置 zh-TW。欢迎大家为 [NetchTranslation](https://github.com/NetchX/NetchTranslation) 提供其他语言的翻译

View File

@@ -1,48 +0,0 @@
# 新手入门
**Version : 1.5.1**
[下载地址](https://github.com/NetchX/Netch/releases)
## 主界面
![主界面](screenshots/main.zh-CN.png)
![主界面部分功能介绍](screenshots/main.zh-CN2.png)
## 设置界面
![设置](screenshots/set.png)
## 添加服务器
> Netch 目前仅支持以下代理协议ShadowsocksVMessSocks5ShadowsockRTrojan。
首先,点击`服务器`增加所需服务器
<img width="80%" height="80%" src="screenshots/addServer.zh-CN.png">
可手动添加单个服务器,或者通过剪切板链接添加单个服务器。也可通过订阅链接批量添加。
点击 `订阅` ` 管理订阅链接` 进入以下界面。
<img width="80%" height="80%" src="screenshots/addLink.zh-CN.png">
填写备注与链接,然后保存即可。保存后在主界面点击 `订阅` ` 从订阅链接更新服务器`。完成服务器添加。添加完服务器后可对服务器进行修改,删除和测速。
## 选择模式
> 此处需要会一点英语,比如你应该知道 `吃鸡` 的英文名称是 `PlayerUnknown's Battlegrounds`
模式选择框有搜索功能,在模式框里输入字符即可搜索,使用英文名称进行搜索,搜索到所需的模式后单击选择,启用模式。相对应的游戏即可被加速
若没有所需的模式,请选择 `[3] Bypass LAN and China (TUN/TAP)` 的模式。此模式需要安装 [TAP-Windows](https://github.com/OpenVPN/tap-windows) 适配器,如果 Netch 提示没有该适配器,可以直接安装 [TAP-Windows](https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe) 来获得该适配器
关于更多的模式说明,详见 [进阶用法](Advanced_Usage.zh-CN.md)。
选择完模式后,点击启用,游戏已被代理。这一步需在开启游戏前完成。
## 配置说明
在设置界面填写完快捷配置数量后即可在主界面进行配置,填入配置名,选择相应的服务器和游戏模式,按下 `Ctrl` 与鼠标左键,即可保存当前配置。下次使用时,点击配置名即可快速启用。
~~ 如果你还觉得不会用,可以去用 SSTap (逃~~

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

2
modes

Submodule modes updated: dd436b3652...72ad96d018