mirror of
https://github.com/netchx/netch.git
synced 2026-05-11 23:45:06 +08:00
Compare commits
41 Commits
1.8.2
...
1.8.3-Beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a3a1e3664 | ||
|
|
3f4a31dac8 | ||
|
|
7c0088cc7f | ||
|
|
1a28d791b7 | ||
|
|
05df786ab6 | ||
|
|
1ffbae6135 | ||
|
|
8ca9d6d9be | ||
|
|
4f1ae20b9b | ||
|
|
15a1db3b21 | ||
|
|
54243a80e7 | ||
|
|
947bf2b3ca | ||
|
|
2a165c79df | ||
|
|
55280df299 | ||
|
|
af48e7119e | ||
|
|
a1b978a22c | ||
|
|
d08a9d5bfd | ||
|
|
c69c40750a | ||
|
|
77376502b7 | ||
|
|
fdfc3f11eb | ||
|
|
0d956efac6 | ||
|
|
3f9709167d | ||
|
|
425e468f78 | ||
|
|
a485a4647c | ||
|
|
0b484face4 | ||
|
|
e0b5b0e49c | ||
|
|
f519850ffc | ||
|
|
4513a68e73 | ||
|
|
95de42e778 | ||
|
|
18168c3a4e | ||
|
|
77f2b761fc | ||
|
|
9bd02ec122 | ||
|
|
cfb4a5b3f6 | ||
|
|
6178045f15 | ||
|
|
4773de99e5 | ||
|
|
eb713db867 | ||
|
|
15f4895c0f | ||
|
|
afbda60dfb | ||
|
|
f51229f2c8 | ||
|
|
26f9ae3958 | ||
|
|
dfc680f0b7 | ||
|
|
5e56556534 |
30
.github/workflows/ci.yml
vendored
Normal file
30
.github/workflows/ci.yml
vendored
Normal 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
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
name: Netch CI
|
name: Netch Release
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*.*'
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
@@ -17,14 +20,6 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: .\BUILD.ps1
|
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
|
- name: Package
|
||||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
@@ -39,10 +34,8 @@ jobs:
|
|||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
with:
|
with:
|
||||||
name: ${{ env.GITHUB_TAG_NAME }}
|
prerelease: ${{ contains(github.ref, '-') }}
|
||||||
prerelease: true
|
|
||||||
draft: false
|
draft: false
|
||||||
files: |
|
files: |
|
||||||
C:\builtfiles\Netch.7z
|
C:\builtfiles\Netch.7z
|
||||||
@@ -55,4 +48,4 @@ jobs:
|
|||||||
## 校验和
|
## 校验和
|
||||||
| 文件名 | SHA256 |
|
| 文件名 | SHA256 |
|
||||||
| :- | :- |
|
| :- | :- |
|
||||||
| Netch.7z | ${{ env.Netch_SHA256 }} |
|
| Netch.7z | ${{ env.Netch_SHA256 }} |
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
/.vs
|
.vs/
|
||||||
/packages
|
|
||||||
.idea/
|
.idea/
|
||||||
/*.user
|
*/bin/
|
||||||
|
*/obj/
|
||||||
|
*.csproj.user
|
||||||
3
Netch/.gitignore
vendored
3
Netch/.gitignore
vendored
@@ -1,3 +0,0 @@
|
|||||||
/bin
|
|
||||||
/obj
|
|
||||||
/Netch.csproj.user
|
|
||||||
@@ -24,7 +24,6 @@ namespace Netch.Controllers
|
|||||||
public void Start(in Mode mode)
|
public void Start(in Mode mode)
|
||||||
{
|
{
|
||||||
PrivoxyController.Start(MainController.Server!);
|
PrivoxyController.Start(MainController.Server!);
|
||||||
Global.Job.AddProcess(PrivoxyController.Instance!);
|
|
||||||
string? pacUrl = null;
|
string? pacUrl = null;
|
||||||
|
|
||||||
if (MainController.Server is Socks5 or Trojan && mode.BypassChina || (Global.Settings.AlwaysStartPACServer ?? false))
|
if (MainController.Server is Socks5 or Trojan && mode.BypassChina || (Global.Settings.AlwaysStartPACServer ?? false))
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Netch.Models;
|
using Netch.Models;
|
||||||
using Netch.Servers.Socks5;
|
using Netch.Servers.Socks5;
|
||||||
using Netch.Utils;
|
using Netch.Utils;
|
||||||
using static Netch.Utils.PortHelper;
|
|
||||||
|
|
||||||
namespace Netch.Controllers
|
namespace Netch.Controllers
|
||||||
{
|
{
|
||||||
@@ -105,20 +104,11 @@ namespace Netch.Controllers
|
|||||||
{
|
{
|
||||||
controller = ServerHelper.GetUtilByTypeName(server.Type).GetController();
|
controller = ServerHelper.GetUtilByTypeName(server.Type).GetController();
|
||||||
|
|
||||||
if (controller is Guard instanceController)
|
TryReleaseTcpPort(controller.Socks5LocalPort(), "Socks5");
|
||||||
Utils.Utils.KillProcessByName(instanceController.MainFile);
|
|
||||||
|
|
||||||
PortCheck(controller.Socks5LocalPort(), "Socks5");
|
|
||||||
|
|
||||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", controller.Name));
|
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", controller.Name));
|
||||||
|
|
||||||
controller.Start(in server, mode);
|
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)
|
if (server is Socks5 socks5)
|
||||||
{
|
{
|
||||||
@@ -133,19 +123,17 @@ namespace Netch.Controllers
|
|||||||
|
|
||||||
private static void StartMode(Mode mode)
|
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)
|
if (ModeController == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (port != null)
|
if (port != null)
|
||||||
PortCheck((ushort) port, portName, portType);
|
TryReleaseTcpPort((ushort) port, portName);
|
||||||
|
|
||||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
|
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
|
||||||
|
|
||||||
ModeController.Start(mode);
|
ModeController.Start(mode);
|
||||||
if (ModeController is Guard {Instance: { }} guard)
|
|
||||||
Global.Job.AddProcess(guard.Instance!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task StopAsync()
|
public static async Task StopAsync()
|
||||||
@@ -189,7 +177,7 @@ namespace Netch.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CheckPort(port, portType);
|
PortHelper.CheckPort(port, portType);
|
||||||
}
|
}
|
||||||
catch (PortInUseException)
|
catch (PortInUseException)
|
||||||
{
|
{
|
||||||
@@ -200,6 +188,26 @@ namespace Netch.Controllers
|
|||||||
throw new MessageException(i18N.TranslateFormat("The {0} port is reserved by system.", $"{portName} ({port})"));
|
throw new MessageException(i18N.TranslateFormat("The {0} port is reserved by system.", $"{portName} ({port})"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void TryReleaseTcpPort(ushort port, string portName)
|
||||||
|
{
|
||||||
|
foreach (var p in PortHelper.GetProcessByUsedTcpPort(port))
|
||||||
|
{
|
||||||
|
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
|
public class MessageException : Exception
|
||||||
|
|||||||
@@ -17,57 +17,30 @@ namespace Netch.Controllers
|
|||||||
{
|
{
|
||||||
private static readonly ServiceController NFService = new("netfilter2");
|
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 readonly string SystemDriver = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
|
||||||
|
|
||||||
static NFController()
|
|
||||||
{
|
|
||||||
string fileName;
|
|
||||||
switch ($"{Environment.OSVersion.Version.Major}.{Environment.OSVersion.Version.Minor}")
|
|
||||||
{
|
|
||||||
case "10.0":
|
|
||||||
case "6.3":
|
|
||||||
case "6.2":
|
|
||||||
case "6.1":
|
|
||||||
case "6.0":
|
|
||||||
fileName = "nfdriver.sys";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new MessageException($"不支持的系统版本:{Environment.OSVersion.Version}");
|
|
||||||
}
|
|
||||||
|
|
||||||
BinDriver = "bin\\" + fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Name { get; } = "Redirector";
|
public string Name { get; } = "Redirector";
|
||||||
|
|
||||||
public void Start(in Mode mode)
|
public void Start(in Mode mode)
|
||||||
{
|
{
|
||||||
CheckDriver();
|
CheckDriver();
|
||||||
|
|
||||||
#region aio_dial
|
aio_dial((int) NameList.TYPE_FILTERLOOPBACK, "false");
|
||||||
|
aio_dial((int) NameList.TYPE_TCPLISN, Global.Settings.RedirectorTCPPort.ToString());
|
||||||
|
|
||||||
aio_dial((int)NameList.TYPE_FILTERLOOPBACK, "false");
|
// Server
|
||||||
aio_dial((int)NameList.TYPE_TCPLISN, Global.Settings.RedirectorTCPPort.ToString());
|
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());
|
||||||
|
dial_Server(Global.Settings.ProcessProxyProtocol);
|
||||||
|
|
||||||
aio_dial((int)NameList.TYPE_FILTERUDP, (Global.Settings.ProcessProxyProtocol != PortType.TCP).ToString().ToLower());
|
// Mode Rule
|
||||||
aio_dial((int)NameList.TYPE_FILTERTCP, (Global.Settings.ProcessProxyProtocol != PortType.UDP).ToString().ToLower());
|
dial_Name(mode);
|
||||||
SetServer(Global.Settings.ProcessProxyProtocol);
|
|
||||||
|
|
||||||
if (!CheckRule(mode.FullRule, out var list))
|
// Features
|
||||||
throw new MessageException($"\"{string.Join("", list.Select(s => s + "\n"))}\" does not conform to C++ regular expression syntax");
|
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 : "");
|
||||||
SetName(mode);
|
aio_dial((int) NameList.TYPE_FILTERCHILDPROC, Global.Settings.ChildProcessHandle.ToString().ToLower());
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
if (Global.Settings.RedirectDNS)
|
|
||||||
aio_dial((int)NameList.TYPE_REDIRCTOR_DNS, Global.Settings.RedirectDNSAddr.ToString());
|
|
||||||
|
|
||||||
if (Global.Settings.RedirectICMP)
|
|
||||||
aio_dial((int)NameList.TYPE_REDIRCTOR_ICMP, Global.Settings.RedirectICMPAddr.ToString());
|
|
||||||
|
|
||||||
aio_dial((int)NameList.TYPE_FILTERCHILDPROC, Global.Settings.ChildProcessHandle.ToString());
|
|
||||||
|
|
||||||
if (!aio_init())
|
if (!aio_init())
|
||||||
throw new MessageException("Redirector Start failed, run Netch with \"-console\" argument");
|
throw new MessageException("Redirector Start failed, run Netch with \"-console\" argument");
|
||||||
@@ -78,83 +51,54 @@ namespace Netch.Controllers
|
|||||||
aio_free();
|
aio_free();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
#region CheckRule
|
||||||
/// </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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="r"></param>
|
/// <param name="r"></param>
|
||||||
/// <param name="clear"></param>
|
/// <param name="clear"></param>
|
||||||
/// <returns>No Problem true</returns>
|
/// <returns>No Problem true</returns>
|
||||||
public static bool CheckCppRegex(string r, bool clear = true)
|
private static bool CheckCppRegex(string r, bool clear = true)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (r.StartsWith("!"))
|
if (r.StartsWith("!"))
|
||||||
return aio_dial((int)NameList.TYPE_ADDNAME, r.Substring(1));
|
return aio_dial((int) NameList.TYPE_ADDNAME, r.Substring(1));
|
||||||
|
|
||||||
return aio_dial((int)NameList.TYPE_ADDNAME, r);
|
return aio_dial((int) NameList.TYPE_ADDNAME, r);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (clear)
|
if (clear)
|
||||||
aio_dial((int)NameList.TYPE_CLRNAME, "");
|
aio_dial((int) NameList.TYPE_CLRNAME, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
results = rules.Where(r => !CheckCppRegex(r, false));
|
||||||
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
|
aio_dial((int) NameList.TYPE_CLRNAME, "");
|
||||||
|
return !results.Any();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
if (portType == PortType.Both)
|
||||||
{
|
{
|
||||||
SetServer(PortType.TCP);
|
dial_Server(PortType.TCP);
|
||||||
SetServer(PortType.UDP);
|
dial_Server(PortType.UDP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,105 +121,97 @@ namespace Netch.Controllers
|
|||||||
|
|
||||||
if (server is Socks5 socks5)
|
if (server is Socks5 socks5)
|
||||||
{
|
{
|
||||||
aio_dial((int)NameList.TYPE_TCPTYPE + offset, "Socks5");
|
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Socks5");
|
||||||
aio_dial((int)NameList.TYPE_TCPHOST + offset, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
|
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
|
||||||
aio_dial((int)NameList.TYPE_TCPUSER + offset, socks5.Username ?? string.Empty);
|
aio_dial((int) NameList.TYPE_TCPUSER + offset, socks5.Username ?? string.Empty);
|
||||||
aio_dial((int)NameList.TYPE_TCPPASS + offset, socks5.Password ?? string.Empty);
|
aio_dial((int) NameList.TYPE_TCPPASS + offset, socks5.Password ?? string.Empty);
|
||||||
aio_dial((int)NameList.TYPE_TCPMETH + offset, string.Empty);
|
aio_dial((int) NameList.TYPE_TCPMETH + offset, string.Empty);
|
||||||
}
|
}
|
||||||
else if (server is Shadowsocks shadowsocks && !shadowsocks.HasPlugin() && Global.Settings.RedirectorSS)
|
else if (server is Shadowsocks shadowsocks && !shadowsocks.HasPlugin() && Global.Settings.RedirectorSS)
|
||||||
{
|
{
|
||||||
aio_dial((int)NameList.TYPE_TCPTYPE + offset, "Shadowsocks");
|
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Shadowsocks");
|
||||||
aio_dial((int)NameList.TYPE_TCPHOST + offset, $"{shadowsocks.AutoResolveHostname()}:{shadowsocks.Port}");
|
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_TCPMETH + offset, shadowsocks.EncryptMethod);
|
||||||
aio_dial((int)NameList.TYPE_TCPPASS + offset, shadowsocks.Password ?? string.Empty);
|
aio_dial((int) NameList.TYPE_TCPPASS + offset, shadowsocks.Password);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
aio_dial((int)NameList.TYPE_TCPTYPE + offset, "Socks5");
|
aio_dial((int) NameList.TYPE_TCPTYPE + offset, "Socks5");
|
||||||
aio_dial((int)NameList.TYPE_TCPHOST + offset, $"127.0.0.1:{controller.Socks5LocalPort()}");
|
aio_dial((int) NameList.TYPE_TCPHOST + offset, $"127.0.0.1:{controller.Socks5LocalPort()}");
|
||||||
aio_dial((int)NameList.TYPE_TCPUSER + offset, string.Empty);
|
aio_dial((int) NameList.TYPE_TCPUSER + offset, string.Empty);
|
||||||
aio_dial((int)NameList.TYPE_TCPPASS + offset, string.Empty);
|
aio_dial((int) NameList.TYPE_TCPPASS + offset, string.Empty);
|
||||||
aio_dial((int)NameList.TYPE_TCPMETH + offset, string.Empty);
|
aio_dial((int) NameList.TYPE_TCPMETH + offset, string.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetName(Mode mode)
|
private void dial_Name(Mode mode)
|
||||||
{
|
{
|
||||||
aio_dial((int)NameList.TYPE_CLRNAME, "");
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
aio_dial((int)NameList.TYPE_ADDNAME, rule);
|
if (!aio_dial((int) NameList.TYPE_ADDNAME, s))
|
||||||
|
list.Add(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
aio_dial((int)NameList.TYPE_ADDNAME, @"NTT\.exe");
|
if (list.Any())
|
||||||
aio_dial((int)NameList.TYPE_BYPNAME, "^" + Global.NetchDir.ToRegexString() + @"((?!NTT\.exe).)*$");
|
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;
|
private static void CheckDriver()
|
||||||
|
|
||||||
[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
|
var binFileVersion = Utils.Utils.GetFileVersion(BinDriver);
|
||||||
TYPE_FILTERLOOPBACK,
|
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
|
||||||
TYPE_FILTERTCP,
|
|
||||||
TYPE_FILTERUDP,
|
|
||||||
TYPE_FILTERIP,
|
|
||||||
TYPE_FILTERCHILDPROC,//子进程捕获
|
|
||||||
|
|
||||||
TYPE_TCPLISN,
|
Logging.Info("内置驱动版本: " + binFileVersion);
|
||||||
TYPE_TCPTYPE,
|
Logging.Info("系统驱动版本: " + systemFileVersion);
|
||||||
TYPE_TCPHOST,
|
|
||||||
TYPE_TCPUSER,
|
|
||||||
TYPE_TCPPASS,
|
|
||||||
TYPE_TCPMETH,
|
|
||||||
|
|
||||||
TYPE_UDPTYPE,
|
if (!File.Exists(SystemDriver))
|
||||||
TYPE_UDPHOST,
|
{
|
||||||
TYPE_UDPUSER,
|
// Install
|
||||||
TYPE_UDPPASS,
|
InstallDriver();
|
||||||
TYPE_UDPMETH,
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TYPE_ADDNAME,
|
var reinstall = false;
|
||||||
TYPE_ADDFIP,
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
TYPE_BYPNAME,
|
if (!reinstall)
|
||||||
|
return;
|
||||||
|
|
||||||
TYPE_CLRNAME,
|
Logging.Info("更新驱动");
|
||||||
TYPE_CLRFIP,
|
UninstallDriver();
|
||||||
|
InstallDriver();
|
||||||
//str addr x.x.x.x only ipv4
|
|
||||||
TYPE_REDIRCTOR_DNS,
|
|
||||||
TYPE_REDIRCTOR_ICMP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Utils
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 安装 NF 驱动
|
/// 安装 NF 驱动
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -341,5 +277,61 @@ namespace Netch.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ namespace Netch.Controllers
|
|||||||
Global.MainForm.BeginInvoke(new Action(() =>
|
Global.MainForm.BeginInvoke(new Action(() =>
|
||||||
{
|
{
|
||||||
if (!_form!.IsDisposed)
|
if (!_form!.IsDisposed)
|
||||||
_form!.richTextBox1.AppendText(line + "\n");
|
_form.richTextBox1.AppendText(line + "\n");
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -18,8 +19,8 @@ namespace Netch.Controllers
|
|||||||
public const string Name = @"Netch";
|
public const string Name = @"Netch";
|
||||||
public const string Copyright = @"Copyright © 2019 - 2021";
|
public const string Copyright = @"Copyright © 2019 - 2021";
|
||||||
|
|
||||||
public const string AssemblyVersion = @"1.8.2";
|
public const string AssemblyVersion = @"1.8.3";
|
||||||
private const string Suffix = @"";
|
private const string Suffix = @"Beta2";
|
||||||
|
|
||||||
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
|
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
|
||||||
|
|
||||||
@@ -89,5 +90,19 @@ namespace Netch.Controllers
|
|||||||
fileName = match.Groups["filename"].Value;
|
fileName = match.Groups["filename"].Value;
|
||||||
sha256 = match.Groups["sha256"].Value;
|
sha256 = match.Groups["sha256"].Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetLatestReleaseContent()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (string l in LatestRelease.body.GetLines(false).SkipWhile(l => l.FirstOrDefault() != '#'))
|
||||||
|
{
|
||||||
|
if (l.Contains("校验和"))
|
||||||
|
break;
|
||||||
|
|
||||||
|
sb.AppendLine(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
Netch/Forms/MainForm.Designer.cs
generated
4
Netch/Forms/MainForm.Designer.cs
generated
@@ -423,7 +423,7 @@
|
|||||||
this.ModeComboBox.Size = new System.Drawing.Size(546, 24);
|
this.ModeComboBox.Size = new System.Drawing.Size(546, 24);
|
||||||
this.ModeComboBox.TabIndex = 2;
|
this.ModeComboBox.TabIndex = 2;
|
||||||
this.ModeComboBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.ComboBox_DrawItem);
|
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
|
// ServerComboBox
|
||||||
//
|
//
|
||||||
@@ -438,7 +438,7 @@
|
|||||||
this.ServerComboBox.Size = new System.Drawing.Size(546, 24);
|
this.ServerComboBox.Size = new System.Drawing.Size(546, 24);
|
||||||
this.ServerComboBox.TabIndex = 1;
|
this.ServerComboBox.TabIndex = 1;
|
||||||
this.ServerComboBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.ComboBox_DrawItem);
|
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
|
// tableLayoutPanel2
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ namespace Netch.Forms
|
|||||||
|
|
||||||
private readonly Dictionary<string, object> _mainFormText = new();
|
private readonly Dictionary<string, object> _mainFormText = new();
|
||||||
|
|
||||||
private bool _comboBoxInitialized;
|
|
||||||
private bool _textRecorded;
|
private bool _textRecorded;
|
||||||
|
|
||||||
public MainForm()
|
public MainForm()
|
||||||
@@ -85,6 +84,8 @@ namespace Netch.Forms
|
|||||||
|
|
||||||
private void MainForm_Load(object sender, EventArgs e)
|
private void MainForm_Load(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
Netch.TimePoint("MainForm ctor (Pre MainForm Load)");
|
||||||
|
|
||||||
// 计算 ComboBox绘制 目标宽度
|
// 计算 ComboBox绘制 目标宽度
|
||||||
RecordSize();
|
RecordSize();
|
||||||
|
|
||||||
@@ -93,7 +94,6 @@ namespace Netch.Forms
|
|||||||
|
|
||||||
ModeHelper.Load();
|
ModeHelper.Load();
|
||||||
LoadModes();
|
LoadModes();
|
||||||
_comboBoxInitialized = true;
|
|
||||||
|
|
||||||
// 加载翻译
|
// 加载翻译
|
||||||
TranslateControls();
|
TranslateControls();
|
||||||
@@ -121,6 +121,8 @@ namespace Netch.Forms
|
|||||||
if (Global.Settings.StartWhenOpened)
|
if (Global.Settings.StartWhenOpened)
|
||||||
ControlButton_Click(null, null);
|
ControlButton_Click(null, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Netch.TimePoint("Post Form Load", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RecordSize()
|
private void RecordSize()
|
||||||
@@ -577,7 +579,8 @@ namespace Netch.Forms
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MessageBoxX.Show(i18N.Translate("Download and install now?"), confirm: true) != DialogResult.OK)
|
if (MessageBoxX.Show(i18N.Translate($"Download and install now?\n\n{UpdateChecker.GetLatestReleaseContent()}"), confirm: true) !=
|
||||||
|
DialogResult.OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
NotifyTip(i18N.Translate("Start downloading new version"));
|
NotifyTip(i18N.Translate("Start downloading new version"));
|
||||||
@@ -735,13 +738,9 @@ namespace Netch.Forms
|
|||||||
|
|
||||||
private void LoadServers()
|
private void LoadServers()
|
||||||
{
|
{
|
||||||
var comboBoxInitialized = _comboBoxInitialized;
|
|
||||||
_comboBoxInitialized = false;
|
|
||||||
|
|
||||||
ServerComboBox.Items.Clear();
|
ServerComboBox.Items.Clear();
|
||||||
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
|
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
|
||||||
SelectLastServer();
|
SelectLastServer();
|
||||||
_comboBoxInitialized = comboBoxInitialized;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SelectLastServer()
|
public void SelectLastServer()
|
||||||
@@ -756,11 +755,8 @@ namespace Netch.Forms
|
|||||||
// 如果当前 ServerComboBox 中没元素,不做处理
|
// 如果当前 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;
|
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -859,14 +855,10 @@ namespace Netch.Forms
|
|||||||
|
|
||||||
public void LoadModes()
|
public void LoadModes()
|
||||||
{
|
{
|
||||||
var comboBoxInitialized = _comboBoxInitialized;
|
|
||||||
_comboBoxInitialized = false;
|
|
||||||
|
|
||||||
ModeComboBox.Items.Clear();
|
ModeComboBox.Items.Clear();
|
||||||
ModeComboBox.Items.AddRange(Global.Modes.ToArray());
|
ModeComboBox.Items.AddRange(Global.Modes.Cast<object>().ToArray());
|
||||||
ModeComboBox.Tag = null;
|
ModeComboBox.Tag = null;
|
||||||
SelectLastMode();
|
SelectLastMode();
|
||||||
_comboBoxInitialized = comboBoxInitialized;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SelectLastMode()
|
public void SelectLastMode()
|
||||||
@@ -881,11 +873,8 @@ namespace Netch.Forms
|
|||||||
// 如果当前 ModeComboBox 中没元素,不做处理
|
// 如果当前 ModeComboBox 中没元素,不做处理
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ModeComboBox_SelectedIndexChanged(object sender, EventArgs o)
|
private void ModeComboBox_SelectionChangeCommitted(object sender, EventArgs o)
|
||||||
{
|
{
|
||||||
if (!_comboBoxInitialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Global.Settings.ModeComboBoxSelectedIndex = Global.Modes.IndexOf((Models.Mode) ModeComboBox.SelectedItem);
|
Global.Settings.ModeComboBoxSelectedIndex = Global.Modes.IndexOf((Models.Mode) ModeComboBox.SelectedItem);
|
||||||
|
|||||||
123
Netch/Forms/Mode/Process.Designer.cs
generated
123
Netch/Forms/Mode/Process.Designer.cs
generated
@@ -31,25 +31,21 @@ namespace Netch.Forms.Mode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
this.components = new System.ComponentModel.Container();
|
|
||||||
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
|
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
|
||||||
this.RemarkLabel = new System.Windows.Forms.Label();
|
this.RemarkLabel = new System.Windows.Forms.Label();
|
||||||
this.RemarkTextBox = new System.Windows.Forms.TextBox();
|
this.RemarkTextBox = new System.Windows.Forms.TextBox();
|
||||||
this.FilenameLabel = new System.Windows.Forms.Label();
|
this.FilenameLabel = new System.Windows.Forms.Label();
|
||||||
this.FilenameTextBox = new System.Windows.Forms.TextBox();
|
this.FilenameTextBox = new System.Windows.Forms.TextBox();
|
||||||
this.containerControl1 = new System.Windows.Forms.ContainerControl();
|
this.containerControl1 = new System.Windows.Forms.ContainerControl();
|
||||||
this.RuleListBox = new System.Windows.Forms.ListBox();
|
this.RuleRichTextBox = new System.Windows.Forms.RichTextBox();
|
||||||
this.ProcessGroupBox = new System.Windows.Forms.GroupBox();
|
this.ProcessGroupBox = new System.Windows.Forms.GroupBox();
|
||||||
this.ProcessNameTextBox = new System.Windows.Forms.TextBox();
|
|
||||||
this.AddButton = new System.Windows.Forms.Button();
|
|
||||||
this.SelectButton = new System.Windows.Forms.Button();
|
this.SelectButton = new System.Windows.Forms.Button();
|
||||||
this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
|
this.ScanButton = new System.Windows.Forms.Button();
|
||||||
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
this.ValidationButton = new System.Windows.Forms.Button();
|
||||||
this.ControlButton = new System.Windows.Forms.Button();
|
this.ControlButton = new System.Windows.Forms.Button();
|
||||||
this.ConfigurationGroupBox.SuspendLayout();
|
this.ConfigurationGroupBox.SuspendLayout();
|
||||||
this.containerControl1.SuspendLayout();
|
this.containerControl1.SuspendLayout();
|
||||||
this.ProcessGroupBox.SuspendLayout();
|
this.ProcessGroupBox.SuspendLayout();
|
||||||
this.contextMenuStrip.SuspendLayout();
|
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// ConfigurationGroupBox
|
// ConfigurationGroupBox
|
||||||
@@ -60,11 +56,12 @@ namespace Netch.Forms.Mode
|
|||||||
this.ConfigurationGroupBox.Controls.Add(this.FilenameTextBox);
|
this.ConfigurationGroupBox.Controls.Add(this.FilenameTextBox);
|
||||||
this.ConfigurationGroupBox.Controls.Add(this.containerControl1);
|
this.ConfigurationGroupBox.Controls.Add(this.containerControl1);
|
||||||
this.ConfigurationGroupBox.Controls.Add(this.ProcessGroupBox);
|
this.ConfigurationGroupBox.Controls.Add(this.ProcessGroupBox);
|
||||||
this.ConfigurationGroupBox.Controls.Add(this.SelectButton);
|
this.ConfigurationGroupBox.Controls.Add(this.ControlButton);
|
||||||
this.ConfigurationGroupBox.Location = new System.Drawing.Point(12, 12);
|
this.ConfigurationGroupBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.ConfigurationGroupBox.Location = new System.Drawing.Point(12, 5);
|
||||||
this.ConfigurationGroupBox.Name = "ConfigurationGroupBox";
|
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 = 1;
|
this.ConfigurationGroupBox.TabIndex = 0;
|
||||||
this.ConfigurationGroupBox.TabStop = false;
|
this.ConfigurationGroupBox.TabStop = false;
|
||||||
this.ConfigurationGroupBox.Text = "Configuration";
|
this.ConfigurationGroupBox.Text = "Configuration";
|
||||||
//
|
//
|
||||||
@@ -81,7 +78,7 @@ namespace Netch.Forms.Mode
|
|||||||
//
|
//
|
||||||
this.RemarkTextBox.Location = new System.Drawing.Point(84, 22);
|
this.RemarkTextBox.Location = new System.Drawing.Point(84, 22);
|
||||||
this.RemarkTextBox.Name = "RemarkTextBox";
|
this.RemarkTextBox.Name = "RemarkTextBox";
|
||||||
this.RemarkTextBox.Size = new System.Drawing.Size(250, 23);
|
this.RemarkTextBox.Size = new System.Drawing.Size(341, 23);
|
||||||
this.RemarkTextBox.TabIndex = 1;
|
this.RemarkTextBox.TabIndex = 1;
|
||||||
this.RemarkTextBox.TextChanged += new System.EventHandler(this.RemarkTextBox_TextChanged);
|
this.RemarkTextBox.TextChanged += new System.EventHandler(this.RemarkTextBox_TextChanged);
|
||||||
//
|
//
|
||||||
@@ -99,86 +96,76 @@ namespace Netch.Forms.Mode
|
|||||||
this.FilenameTextBox.Location = new System.Drawing.Point(84, 52);
|
this.FilenameTextBox.Location = new System.Drawing.Point(84, 52);
|
||||||
this.FilenameTextBox.Name = "FilenameTextBox";
|
this.FilenameTextBox.Name = "FilenameTextBox";
|
||||||
this.FilenameTextBox.ReadOnly = true;
|
this.FilenameTextBox.ReadOnly = true;
|
||||||
this.FilenameTextBox.Size = new System.Drawing.Size(250, 23);
|
this.FilenameTextBox.Size = new System.Drawing.Size(341, 23);
|
||||||
this.FilenameTextBox.TabIndex = 3;
|
this.FilenameTextBox.TabIndex = 3;
|
||||||
//
|
//
|
||||||
// containerControl1
|
// containerControl1
|
||||||
//
|
//
|
||||||
this.containerControl1.Controls.Add(this.RuleListBox);
|
this.containerControl1.Controls.Add(this.RuleRichTextBox);
|
||||||
this.containerControl1.Location = new System.Drawing.Point(6, 81);
|
this.containerControl1.Location = new System.Drawing.Point(6, 81);
|
||||||
this.containerControl1.Name = "containerControl1";
|
this.containerControl1.Name = "containerControl1";
|
||||||
this.containerControl1.Size = new System.Drawing.Size(328, 176);
|
this.containerControl1.Size = new System.Drawing.Size(419, 221);
|
||||||
this.containerControl1.TabIndex = 5;
|
this.containerControl1.TabIndex = 4;
|
||||||
this.containerControl1.Text = "containerControl1";
|
this.containerControl1.Text = "containerControl1";
|
||||||
//
|
//
|
||||||
// RuleListBox
|
// RuleRichTextBox
|
||||||
//
|
//
|
||||||
this.RuleListBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.RuleRichTextBox.DetectUrls = false;
|
||||||
this.RuleListBox.FormattingEnabled = true;
|
this.RuleRichTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.RuleListBox.ItemHeight = 17;
|
this.RuleRichTextBox.Location = new System.Drawing.Point(0, 0);
|
||||||
this.RuleListBox.Location = new System.Drawing.Point(0, 0);
|
this.RuleRichTextBox.Name = "RuleRichTextBox";
|
||||||
this.RuleListBox.Name = "RuleListBox";
|
this.RuleRichTextBox.Size = new System.Drawing.Size(419, 221);
|
||||||
this.RuleListBox.Size = new System.Drawing.Size(328, 176);
|
this.RuleRichTextBox.TabIndex = 0;
|
||||||
this.RuleListBox.TabIndex = 0;
|
this.RuleRichTextBox.Text = "";
|
||||||
this.RuleListBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.RuleListBox_MouseUp);
|
this.RuleRichTextBox.WordWrap = false;
|
||||||
//
|
//
|
||||||
// ProcessGroupBox
|
// ProcessGroupBox
|
||||||
//
|
//
|
||||||
this.ProcessGroupBox.Controls.Add(this.ProcessNameTextBox);
|
this.ProcessGroupBox.Controls.Add(this.SelectButton);
|
||||||
this.ProcessGroupBox.Controls.Add(this.AddButton);
|
this.ProcessGroupBox.Controls.Add(this.ScanButton);
|
||||||
this.ProcessGroupBox.Location = new System.Drawing.Point(6, 263);
|
this.ProcessGroupBox.Controls.Add(this.ValidationButton);
|
||||||
|
this.ProcessGroupBox.Location = new System.Drawing.Point(6, 295);
|
||||||
this.ProcessGroupBox.Name = "ProcessGroupBox";
|
this.ProcessGroupBox.Name = "ProcessGroupBox";
|
||||||
this.ProcessGroupBox.Size = new System.Drawing.Size(328, 46);
|
this.ProcessGroupBox.Size = new System.Drawing.Size(419, 44);
|
||||||
this.ProcessGroupBox.TabIndex = 6;
|
this.ProcessGroupBox.TabIndex = 5;
|
||||||
this.ProcessGroupBox.TabStop = false;
|
this.ProcessGroupBox.TabStop = false;
|
||||||
//
|
//
|
||||||
// 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;
|
|
||||||
//
|
|
||||||
// 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);
|
|
||||||
//
|
|
||||||
// SelectButton
|
// SelectButton
|
||||||
//
|
//
|
||||||
this.SelectButton.Location = new System.Drawing.Point(6, 315);
|
this.SelectButton.Location = new System.Drawing.Point(6, 13);
|
||||||
this.SelectButton.Name = "SelectButton";
|
this.SelectButton.Name = "SelectButton";
|
||||||
this.SelectButton.Size = new System.Drawing.Size(75, 23);
|
this.SelectButton.Size = new System.Drawing.Size(75, 23);
|
||||||
this.SelectButton.TabIndex = 7;
|
this.SelectButton.TabIndex = 0;
|
||||||
this.SelectButton.Text = "Select";
|
this.SelectButton.Text = "Select";
|
||||||
this.SelectButton.UseVisualStyleBackColor = true;
|
this.SelectButton.UseVisualStyleBackColor = true;
|
||||||
this.SelectButton.Click += new System.EventHandler(this.SelectButton_Click);
|
this.SelectButton.Click += new System.EventHandler(this.SelectButton_Click);
|
||||||
//
|
//
|
||||||
// contextMenuStrip
|
// ScanButton
|
||||||
//
|
//
|
||||||
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
this.ScanButton.Location = new System.Drawing.Point(87, 13);
|
||||||
this.DeleteToolStripMenuItem});
|
this.ScanButton.Name = "ScanButton";
|
||||||
this.contextMenuStrip.Name = "contextMenuStrip";
|
this.ScanButton.Size = new System.Drawing.Size(75, 23);
|
||||||
this.contextMenuStrip.Size = new System.Drawing.Size(114, 26);
|
this.ScanButton.TabIndex = 1;
|
||||||
|
this.ScanButton.Text = "Scan";
|
||||||
|
this.ScanButton.UseVisualStyleBackColor = true;
|
||||||
|
this.ScanButton.Click += new System.EventHandler(this.ScanButton_Click);
|
||||||
//
|
//
|
||||||
// DeleteToolStripMenuItem
|
// ValidationButton
|
||||||
//
|
//
|
||||||
this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem";
|
this.ValidationButton.Location = new System.Drawing.Point(338, 13);
|
||||||
this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(113, 22);
|
this.ValidationButton.Name = "ValidationButton";
|
||||||
this.DeleteToolStripMenuItem.Text = "Delete";
|
this.ValidationButton.Size = new System.Drawing.Size(75, 23);
|
||||||
this.DeleteToolStripMenuItem.Click += new System.EventHandler(this.deleteRule_Click);
|
this.ValidationButton.TabIndex = 2;
|
||||||
|
this.ValidationButton.Text = "Validation";
|
||||||
|
this.ValidationButton.UseVisualStyleBackColor = true;
|
||||||
|
this.ValidationButton.Click += new System.EventHandler(this.ValidationButton_Click);
|
||||||
//
|
//
|
||||||
// ControlButton
|
// ControlButton
|
||||||
//
|
//
|
||||||
this.ControlButton.Location = new System.Drawing.Point(277, 362);
|
this.ControlButton.Location = new System.Drawing.Point(344, 345);
|
||||||
this.ControlButton.Name = "ControlButton";
|
this.ControlButton.Name = "ControlButton";
|
||||||
this.ControlButton.Size = new System.Drawing.Size(75, 23);
|
this.ControlButton.Size = new System.Drawing.Size(75, 23);
|
||||||
this.ControlButton.TabIndex = 2;
|
this.ControlButton.TabIndex = 6;
|
||||||
this.ControlButton.Text = "Save";
|
this.ControlButton.Text = "Save";
|
||||||
this.ControlButton.UseVisualStyleBackColor = true;
|
this.ControlButton.UseVisualStyleBackColor = true;
|
||||||
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
|
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
|
||||||
@@ -187,14 +174,14 @@ namespace Netch.Forms.Mode
|
|||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
|
||||||
this.ClientSize = new System.Drawing.Size(364, 397);
|
this.ClientSize = new System.Drawing.Size(455, 388);
|
||||||
this.Controls.Add(this.ConfigurationGroupBox);
|
this.Controls.Add(this.ConfigurationGroupBox);
|
||||||
this.Controls.Add(this.ControlButton);
|
|
||||||
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.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||||
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||||
this.MaximizeBox = false;
|
this.MaximizeBox = false;
|
||||||
this.Name = "Process";
|
this.Name = "Process";
|
||||||
|
this.Padding = new System.Windows.Forms.Padding(12, 5, 12, 5);
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||||
this.Text = "Create Process Mode";
|
this.Text = "Create Process Mode";
|
||||||
this.Load += new System.EventHandler(this.ModeForm_Load);
|
this.Load += new System.EventHandler(this.ModeForm_Load);
|
||||||
@@ -202,27 +189,23 @@ namespace Netch.Forms.Mode
|
|||||||
this.ConfigurationGroupBox.PerformLayout();
|
this.ConfigurationGroupBox.PerformLayout();
|
||||||
this.containerControl1.ResumeLayout(false);
|
this.containerControl1.ResumeLayout(false);
|
||||||
this.ProcessGroupBox.ResumeLayout(false);
|
this.ProcessGroupBox.ResumeLayout(false);
|
||||||
this.ProcessGroupBox.PerformLayout();
|
|
||||||
this.contextMenuStrip.ResumeLayout(false);
|
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.Button ScanButton;
|
||||||
private System.Windows.Forms.ContainerControl containerControl1;
|
private System.Windows.Forms.ContainerControl containerControl1;
|
||||||
private System.Windows.Forms.ContextMenuStrip contextMenuStrip;
|
|
||||||
private System.Windows.Forms.ToolStripMenuItem DeleteToolStripMenuItem;
|
|
||||||
public System.Windows.Forms.GroupBox ConfigurationGroupBox;
|
public System.Windows.Forms.GroupBox ConfigurationGroupBox;
|
||||||
private System.Windows.Forms.Label RemarkLabel;
|
private System.Windows.Forms.Label RemarkLabel;
|
||||||
private System.Windows.Forms.GroupBox ProcessGroupBox;
|
private System.Windows.Forms.GroupBox ProcessGroupBox;
|
||||||
private System.Windows.Forms.ListBox RuleListBox;
|
|
||||||
private System.Windows.Forms.TextBox RemarkTextBox;
|
private System.Windows.Forms.TextBox RemarkTextBox;
|
||||||
private System.Windows.Forms.TextBox ProcessNameTextBox;
|
|
||||||
private System.Windows.Forms.Button AddButton;
|
|
||||||
private System.Windows.Forms.Button SelectButton;
|
private System.Windows.Forms.Button SelectButton;
|
||||||
public System.Windows.Forms.Button ControlButton;
|
public System.Windows.Forms.Button ControlButton;
|
||||||
private System.Windows.Forms.Label FilenameLabel;
|
private System.Windows.Forms.Label FilenameLabel;
|
||||||
private System.Windows.Forms.TextBox FilenameTextBox;
|
private System.Windows.Forms.TextBox FilenameTextBox;
|
||||||
|
private RichTextBox RuleRichTextBox;
|
||||||
|
private Button ValidationButton;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||||
using Netch.Controllers;
|
using Netch.Controllers;
|
||||||
|
using Netch.Models;
|
||||||
using Netch.Properties;
|
using Netch.Properties;
|
||||||
using Netch.Utils;
|
using Netch.Utils;
|
||||||
|
|
||||||
@@ -33,10 +35,24 @@ namespace Netch.Forms.Mode
|
|||||||
_mode = mode;
|
_mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
#region Model
|
||||||
/// 是否被编辑过
|
|
||||||
/// </summary>
|
public IEnumerable<string> Rules => RuleRichTextBox.Lines;
|
||||||
public bool Edited { get; private set; }
|
|
||||||
|
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)
|
public void ModeForm_Load(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
@@ -47,60 +63,10 @@ namespace Netch.Forms.Mode
|
|||||||
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
|
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
|
||||||
RemarkTextBox.Text = _mode.Remark;
|
RemarkTextBox.Text = _mode.Remark;
|
||||||
FilenameTextBox.Text = _mode.RelativePath;
|
FilenameTextBox.Text = _mode.RelativePath;
|
||||||
RuleListBox.Items.AddRange(_mode.Rule.Cast<object>().ToArray());
|
RuleAddRange(_mode.Rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
i18N.TranslateForm(this);
|
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("rule can not be empty"));
|
|
||||||
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 SelectButton_Click(object sender, EventArgs e)
|
private void SelectButton_Click(object sender, EventArgs e)
|
||||||
@@ -108,7 +74,7 @@ namespace Netch.Forms.Mode
|
|||||||
var dialog = new CommonOpenFileDialog
|
var dialog = new CommonOpenFileDialog
|
||||||
{
|
{
|
||||||
IsFolderPicker = true,
|
IsFolderPicker = true,
|
||||||
Multiselect = false,
|
Multiselect = true,
|
||||||
Title = i18N.Translate("Select a folder"),
|
Title = i18N.Translate("Select a folder"),
|
||||||
AddToMostRecentlyUsedList = false,
|
AddToMostRecentlyUsedList = false,
|
||||||
EnsurePathExists = true,
|
EnsurePathExists = true,
|
||||||
@@ -117,17 +83,20 @@ namespace Netch.Forms.Mode
|
|||||||
|
|
||||||
if (dialog.ShowDialog(Handle) == CommonFileDialogResult.Ok)
|
if (dialog.ShowDialog(Handle) == CommonFileDialogResult.Ok)
|
||||||
{
|
{
|
||||||
var path = dialog.FileName;
|
foreach (string p in dialog.FileNames)
|
||||||
if (!path.EndsWith(@"\"))
|
{
|
||||||
path += @"\";
|
string path = p;
|
||||||
|
if (!path.EndsWith(@"\"))
|
||||||
|
path += @"\";
|
||||||
|
|
||||||
RuleListBox.Items.Add(path.ToRegexString());
|
RuleAdd($"^{path.ToRegexString()}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ControlButton_Click(object sender, EventArgs e)
|
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"));
|
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
|
||||||
return;
|
return;
|
||||||
@@ -149,11 +118,9 @@ namespace Netch.Forms.Mode
|
|||||||
{
|
{
|
||||||
_mode.Remark = RemarkTextBox.Text;
|
_mode.Remark = RemarkTextBox.Text;
|
||||||
_mode.Rule.Clear();
|
_mode.Rule.Clear();
|
||||||
_mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
|
_mode.Rule.AddRange(RuleRichTextBox.Lines);
|
||||||
|
|
||||||
_mode.WriteFile();
|
_mode.WriteFile();
|
||||||
Global.MainForm.LoadModes();
|
|
||||||
Edited = false;
|
|
||||||
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
|
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -173,10 +140,9 @@ namespace Netch.Forms.Mode
|
|||||||
Remark = RemarkTextBox.Text
|
Remark = RemarkTextBox.Text
|
||||||
};
|
};
|
||||||
|
|
||||||
mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
|
mode.Rule.AddRange(RuleRichTextBox.Lines);
|
||||||
|
|
||||||
mode.WriteFile();
|
mode.WriteFile();
|
||||||
ModeHelper.Add(mode);
|
|
||||||
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
|
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,5 +156,57 @@ namespace Netch.Forms.Mode
|
|||||||
FilenameTextBox.Text = FilenameTextBox.Text = ModeEditorUtils.GetCustomModeRelativePath(RemarkTextBox.Text);
|
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
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,6 @@ namespace Netch.Forms.Mode
|
|||||||
_mode.Type = (int) comboBox1.SelectedValue;
|
_mode.Type = (int) comboBox1.SelectedValue;
|
||||||
|
|
||||||
_mode.WriteFile();
|
_mode.WriteFile();
|
||||||
Global.MainForm.LoadModes();
|
|
||||||
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
|
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -105,7 +104,6 @@ namespace Netch.Forms.Mode
|
|||||||
mode.Rule.AddRange(richTextBox1.Lines);
|
mode.Rule.AddRange(richTextBox1.Lines);
|
||||||
|
|
||||||
mode.WriteFile();
|
mode.WriteFile();
|
||||||
ModeHelper.Add(mode);
|
|
||||||
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
|
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using System.Text.Encodings.Web;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using WindowsJobAPI;
|
|
||||||
using Netch.Controllers;
|
using Netch.Controllers;
|
||||||
using Netch.Forms;
|
using Netch.Forms;
|
||||||
using Netch.Models;
|
using Netch.Models;
|
||||||
@@ -34,12 +33,6 @@ namespace Netch
|
|||||||
|
|
||||||
public static Mutex Mutex => LazyMutex.Value;
|
public static Mutex Mutex => LazyMutex.Value;
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
public static bool Testing = false;
|
|
||||||
#else
|
|
||||||
public const bool Testing = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用于读取和写入的配置
|
/// 用于读取和写入的配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -50,11 +43,6 @@ namespace Netch
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly List<Mode> Modes = new();
|
public static readonly List<Mode> Modes = new();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Windows Job API
|
|
||||||
/// </summary>
|
|
||||||
public static readonly JobObject Job = new();
|
|
||||||
|
|
||||||
public static class Flags
|
public static class Flags
|
||||||
{
|
{
|
||||||
public static readonly bool IsWindows10Upper = Environment.OSVersion.Version.Major >= 10;
|
public static readonly bool IsWindows10Upper = Environment.OSVersion.Version.Major >= 10;
|
||||||
|
|||||||
@@ -1,54 +1,28 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Netch.Models.GitHubRelease
|
namespace Netch.Models.GitHubRelease
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public struct SuffixVersion : ICloneable, IComparable, IComparable<SuffixVersion>, IEquatable<SuffixVersion>
|
public struct SuffixVersion : IComparable, IComparable<SuffixVersion>
|
||||||
{
|
{
|
||||||
public int Major { get; }
|
public Version Version { get; }
|
||||||
|
|
||||||
public int Minor { get; }
|
public string Suffix { get; }
|
||||||
|
|
||||||
public int Patch { get; }
|
public SuffixVersion(Version version, string suffix)
|
||||||
|
|
||||||
public string PreRelease { get; }
|
|
||||||
|
|
||||||
public int Build { get; }
|
|
||||||
|
|
||||||
public SuffixVersion(int major, int minor, int patch, string preRelease, int build)
|
|
||||||
{
|
{
|
||||||
Major = major;
|
Version = version;
|
||||||
Minor = minor;
|
Suffix = suffix;
|
||||||
Patch = patch;
|
|
||||||
PreRelease = preRelease;
|
|
||||||
Build = build;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SuffixVersion(Version version, string preRelease, int build)
|
|
||||||
{
|
|
||||||
Major = version.Major;
|
|
||||||
Minor = version.Minor;
|
|
||||||
Patch = version.Build;
|
|
||||||
PreRelease = preRelease;
|
|
||||||
Build = build;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SuffixVersion Parse(string input)
|
public static SuffixVersion Parse(string input)
|
||||||
{
|
{
|
||||||
var splitStr = input.Split('-');
|
var split = input.Split('-');
|
||||||
var dotNetVersion = Version.Parse(splitStr[0]);
|
var dotNetVersion = Version.Parse(split[0]);
|
||||||
var preRelease = new StringBuilder();
|
var preRelease = split.ElementAtOrDefault(1) ?? string.Empty;
|
||||||
var build = 0;
|
|
||||||
|
|
||||||
if (splitStr.Length > 1)
|
return new SuffixVersion(dotNetVersion, preRelease);
|
||||||
foreach (var c in splitStr[1])
|
|
||||||
if (int.TryParse(c.ToString(), out var n))
|
|
||||||
build = build * 10 + n;
|
|
||||||
else
|
|
||||||
preRelease.Append(c);
|
|
||||||
|
|
||||||
return new SuffixVersion(dotNetVersion, preRelease.ToString(), build);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryParse(string input, out SuffixVersion result)
|
public static bool TryParse(string input, out SuffixVersion result)
|
||||||
@@ -65,17 +39,12 @@ namespace Netch.Models.GitHubRelease
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Clone()
|
|
||||||
{
|
|
||||||
return new SuffixVersion(Major, Major, Patch, PreRelease, Build);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int CompareTo(object obj)
|
public int CompareTo(object obj)
|
||||||
{
|
{
|
||||||
if (obj is SuffixVersion version)
|
if (obj is not SuffixVersion version)
|
||||||
return CompareTo(version);
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
return -1;
|
return CompareTo(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -86,57 +55,27 @@ namespace Netch.Models.GitHubRelease
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public int CompareTo(SuffixVersion other)
|
public int CompareTo(SuffixVersion other)
|
||||||
{
|
{
|
||||||
var majorComparison = Major.CompareTo(other.Major);
|
var versionComparison = Version.CompareTo(other.Version);
|
||||||
if (majorComparison != 0)
|
if (versionComparison != 0)
|
||||||
return majorComparison;
|
return versionComparison;
|
||||||
|
|
||||||
var minorComparison = Minor.CompareTo(other.Minor);
|
if (Suffix == string.Empty)
|
||||||
if (minorComparison != 0)
|
return other.Suffix == string.Empty ? 0 : 1;
|
||||||
return minorComparison;
|
|
||||||
|
|
||||||
var patchComparison = Patch.CompareTo(other.Patch);
|
if (other.Suffix == string.Empty)
|
||||||
if (patchComparison != 0)
|
|
||||||
return patchComparison;
|
|
||||||
|
|
||||||
if (PreRelease == string.Empty)
|
|
||||||
return other.PreRelease == string.Empty ? 0 : 1;
|
|
||||||
|
|
||||||
if (other.PreRelease == string.Empty)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
var suffixComparison = string.Compare(PreRelease, other.PreRelease, StringComparison.Ordinal);
|
var suffixComparison = string.Compare(Suffix, other.Suffix, StringComparison.OrdinalIgnoreCase);
|
||||||
if (suffixComparison != 0)
|
return suffixComparison;
|
||||||
return suffixComparison;
|
|
||||||
|
|
||||||
return Build.CompareTo(other.Build);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(SuffixVersion other)
|
|
||||||
{
|
|
||||||
return Major == other.Major && Minor == other.Minor && Patch == other.Patch && PreRelease == other.PreRelease && Build == other.Build;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
return obj is SuffixVersion other && Equals(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
unchecked
|
|
||||||
{
|
|
||||||
var hashCode = Major;
|
|
||||||
hashCode = (hashCode * 397) ^ Minor;
|
|
||||||
hashCode = (hashCode * 397) ^ Patch;
|
|
||||||
hashCode = (hashCode * 397) ^ (PreRelease != null ? PreRelease.GetHashCode() : 0);
|
|
||||||
hashCode = (hashCode * 397) ^ Build;
|
|
||||||
return hashCode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"{Major}.{Minor}.{Patch}{(string.IsNullOrEmpty(PreRelease) ? "" : "-")}{PreRelease}{(Build == 0 ? "" : Build.ToString())}";
|
var s = Version.ToString();
|
||||||
|
if (Suffix != string.Empty)
|
||||||
|
s += $"-{Suffix}";
|
||||||
|
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,12 +10,41 @@ namespace Netch.Models
|
|||||||
{
|
{
|
||||||
public class Mode
|
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>
|
||||||
/// 规则
|
/// 规则
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly List<string> Rule = new();
|
public List<string> Rule => _lazyRule.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 绕过中国(0. 不绕过 1. 绕过)
|
/// 绕过中国(0. 不绕过 1. 绕过)
|
||||||
@@ -45,15 +74,6 @@ namespace Netch.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Type { get; set; } = 0;
|
public int Type { get; set; } = 0;
|
||||||
|
|
||||||
public Mode(string fullName)
|
|
||||||
{
|
|
||||||
FullName = fullName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mode()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 文件相对路径(必须是存在的文件)
|
/// 文件相对路径(必须是存在的文件)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -79,7 +99,7 @@ namespace Netch.Models
|
|||||||
relativePath.Replace(">", "");
|
relativePath.Replace(">", "");
|
||||||
relativePath.Replace(".h", ".txt");
|
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)
|
if (mode == null)
|
||||||
throw new MessageException($"{relativePath} file included in {Remark} not found");
|
throw new MessageException($"{relativePath} file included in {Remark} not found");
|
||||||
@@ -105,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)
|
public void WriteFile(string? fullName = null)
|
||||||
{
|
{
|
||||||
if (fullName != null)
|
if (fullName != null)
|
||||||
|
|||||||
121
Netch/Models/ParameterBase.cs
Normal file
121
Netch/Models/ParameterBase.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,15 +16,41 @@ namespace Netch
|
|||||||
{
|
{
|
||||||
public static class 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>
|
||||||
/// 应用程序的主入口点
|
/// 应用程序的主入口点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
AttachConsole();
|
||||||
|
#else
|
||||||
if (args.Contains("-console"))
|
if (args.Contains("-console"))
|
||||||
if (!NativeMethods.AttachConsole(-1))
|
AttachConsole();
|
||||||
NativeMethods.AllocConsole();
|
#endif
|
||||||
|
StartStopwatch("Netch");
|
||||||
|
|
||||||
// 设置当前目录
|
// 设置当前目录
|
||||||
Directory.SetCurrentDirectory(Global.NetchDir);
|
Directory.SetCurrentDirectory(Global.NetchDir);
|
||||||
@@ -40,9 +66,11 @@ namespace Netch
|
|||||||
if (!Directory.Exists(item))
|
if (!Directory.Exists(item))
|
||||||
Directory.CreateDirectory(item);
|
Directory.CreateDirectory(item);
|
||||||
|
|
||||||
|
TimePoint("Clean Old, Create Directory");
|
||||||
// 加载配置
|
// 加载配置
|
||||||
Configuration.Load();
|
Configuration.Load();
|
||||||
|
|
||||||
|
TimePoint("Load Configuration");
|
||||||
// 检查是否已经运行
|
// 检查是否已经运行
|
||||||
if (!Global.Mutex.WaitOne(0, false))
|
if (!Global.Mutex.WaitOne(0, false))
|
||||||
{
|
{
|
||||||
@@ -76,6 +104,8 @@ namespace Netch
|
|||||||
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
|
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
|
||||||
Task.Run(() => { Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}"); });
|
Task.Run(() => { Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}"); });
|
||||||
|
|
||||||
|
TimePoint("Get Info, Pre-Form");
|
||||||
|
|
||||||
// 绑定错误捕获
|
// 绑定错误捕获
|
||||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||||
Application.ThreadException += Application_OnException;
|
Application.ThreadException += Application_OnException;
|
||||||
@@ -85,6 +115,12 @@ namespace Netch
|
|||||||
Application.Run(Global.MainForm);
|
Application.Run(Global.MainForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AttachConsole()
|
||||||
|
{
|
||||||
|
if (!NativeMethods.AttachConsole(-1))
|
||||||
|
NativeMethods.AllocConsole();
|
||||||
|
}
|
||||||
|
|
||||||
public static void Application_OnException(object sender, ThreadExceptionEventArgs e)
|
public static void Application_OnException(object sender, ThreadExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
Logging.Error(e.Exception.ToString());
|
Logging.Error(e.Exception.ToString());
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<!-- <EnableNETAnalyzers>true</EnableNETAnalyzers>
|
||||||
|
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||||
|
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>-->
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
@@ -43,15 +46,18 @@
|
|||||||
<PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" />
|
<PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" />
|
||||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.66" GeneratePathProperty="true" />
|
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.66" GeneratePathProperty="true" />
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
<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.Collections.Immutable" Version="5.0.0" />
|
||||||
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" />
|
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" />
|
||||||
<PackageReference Include="System.Text.Json" Version="5.0.1" />
|
<PackageReference Include="System.Text.Json" Version="5.0.1" />
|
||||||
<PackageReference Include="TaskScheduler" Version="2.9.1" />
|
<PackageReference Include="TaskScheduler" Version="2.9.1" />
|
||||||
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.5" />
|
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.6" />
|
||||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
|
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
|
||||||
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.5" />
|
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.6" />
|
||||||
<PackageReference Include="WindowsFirewallHelper" Version="2.0.4.70-beta2" />
|
<PackageReference Include="WindowsFirewallHelper" Version="2.0.4.70-beta2" />
|
||||||
<PackageReference Include="WindowsJobAPI" Version="5.0.1" />
|
|
||||||
<PackageReference Include="WindowsProxy" Version="5.0.3" />
|
<PackageReference Include="WindowsProxy" Version="5.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
64
Netch/Properties/Resources.Designer.cs
generated
64
Netch/Properties/Resources.Designer.cs
generated
@@ -1,10 +1,10 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// <auto-generated>
|
// <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>
|
// </auto-generated>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -13,13 +13,13 @@ namespace Netch.Properties {
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
/// 一个强类型的资源类,用于查找本地化的字符串等。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
// 此类是由 StronglyTypedResourceBuilder
|
||||||
// class via a tool like ResGen or Visual Studio.
|
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||||
// with the /str option, or rebuild your VS project.
|
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
internal class Resources {
|
internal class Resources {
|
||||||
@@ -33,7 +33,7 @@ namespace Netch.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the cached ResourceManager instance used by this class.
|
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
@@ -47,8 +47,8 @@ namespace Netch.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Overrides the current thread's CurrentUICulture property for all
|
/// 重写当前线程的 CurrentUICulture 属性,对
|
||||||
/// resource lookups using this strongly typed resource class.
|
/// 使用此强类型资源类的所有资源查找执行重写。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
internal static global::System.Globalization.CultureInfo Culture {
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
@@ -61,7 +61,7 @@ namespace Netch.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Byte[].
|
/// 查找 System.Byte[] 类型的本地化资源。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static byte[] _7za {
|
internal static byte[] _7za {
|
||||||
get {
|
get {
|
||||||
@@ -71,17 +71,7 @@ namespace Netch.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Byte[].
|
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||||
/// </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.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static System.Drawing.Bitmap CopyLink {
|
internal static System.Drawing.Bitmap CopyLink {
|
||||||
get {
|
get {
|
||||||
@@ -91,17 +81,7 @@ namespace Netch.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Byte[].
|
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||||
/// </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.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static System.Drawing.Bitmap delete {
|
internal static System.Drawing.Bitmap delete {
|
||||||
get {
|
get {
|
||||||
@@ -111,7 +91,7 @@ namespace Netch.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static System.Drawing.Bitmap edit {
|
internal static System.Drawing.Bitmap edit {
|
||||||
get {
|
get {
|
||||||
@@ -121,7 +101,7 @@ namespace Netch.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
/// 查找类似于 (图标) 的 System.Drawing.Icon 类型的本地化资源。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static System.Drawing.Icon icon {
|
internal static System.Drawing.Icon icon {
|
||||||
get {
|
get {
|
||||||
@@ -131,7 +111,7 @@ namespace Netch.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static System.Drawing.Bitmap Netch {
|
internal static System.Drawing.Bitmap Netch {
|
||||||
get {
|
get {
|
||||||
@@ -141,7 +121,7 @@ namespace Netch.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static System.Drawing.Bitmap speed {
|
internal static System.Drawing.Bitmap speed {
|
||||||
get {
|
get {
|
||||||
@@ -151,7 +131,7 @@ namespace Netch.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static System.Drawing.Bitmap Sponsor {
|
internal static System.Drawing.Bitmap Sponsor {
|
||||||
get {
|
get {
|
||||||
@@ -161,7 +141,7 @@ namespace Netch.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Byte[].
|
/// 查找 System.Byte[] 类型的本地化资源。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static byte[] zh_CN {
|
internal static byte[] zh_CN {
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -118,10 +118,6 @@
|
|||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
<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">
|
<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,
|
<value>..\Resources\zh-CN;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
|
||||||
PublicKeyToken=b77a5c561934e089</value>
|
PublicKeyToken=b77a5c561934e089</value>
|
||||||
@@ -150,10 +146,6 @@
|
|||||||
<value>..\Resources\CopyLink.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
|
<value>..\Resources\CopyLink.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral,
|
||||||
PublicKeyToken=b03f5f7f11d50a3a</value>
|
PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</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">
|
<data name="7za" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\7za.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
|
<value>..\Resources\7za.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
|
||||||
PublicKeyToken=b77a5c561934e089</value>
|
PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
|||||||
Binary file not shown.
@@ -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
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Netch.Forms;
|
using Netch.Forms;
|
||||||
|
using Netch.Utils;
|
||||||
|
|
||||||
namespace Netch.Servers.Shadowsocks.Form
|
namespace Netch.Servers.Shadowsocks.Form
|
||||||
{
|
{
|
||||||
@@ -8,7 +9,7 @@ namespace Netch.Servers.Shadowsocks.Form
|
|||||||
{
|
{
|
||||||
server ??= new Shadowsocks();
|
server ??= new Shadowsocks();
|
||||||
Server = server;
|
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);
|
CreateComboBox("EncryptMethod", "Encrypt Method", SSGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod);
|
||||||
CreateTextBox("Plugin", "Plugin", s => true, s => server.Plugin = s, server.Plugin);
|
CreateTextBox("Plugin", "Plugin", s => true, s => server.Plugin = s, server.Plugin);
|
||||||
CreateTextBox("PluginsOption", "Plugin Options", s => true, s => server.PluginOption = s, server.PluginOption);
|
CreateTextBox("PluginsOption", "Plugin Options", s => true, s => server.PluginOption = s, server.PluginOption);
|
||||||
|
|||||||
@@ -24,26 +24,60 @@ namespace Netch.Servers.Shadowsocks
|
|||||||
{
|
{
|
||||||
var server = (Shadowsocks) s;
|
var server = (Shadowsocks) s;
|
||||||
|
|
||||||
#region Argument
|
var command = new SSParameter
|
||||||
|
{
|
||||||
var argument = new StringBuilder();
|
s = server.AutoResolveHostname(),
|
||||||
argument.Append($"-s {server.AutoResolveHostname()} " + $"-p {server.Port} " + $"-b {this.LocalAddress()} " +
|
p = server.Port,
|
||||||
$"-l {this.Socks5LocalPort()} " + $"-m {server.EncryptMethod} " + $"-k \"{server.Password}\" " + "-u");
|
b = this.LocalAddress(),
|
||||||
|
l = this.Socks5LocalPort(),
|
||||||
if (!string.IsNullOrWhiteSpace(server.Plugin) && !string.IsNullOrWhiteSpace(server.PluginOption))
|
m = server.EncryptMethod,
|
||||||
argument.Append($" --plugin {server.Plugin}" + $" --plugin-opts \"{server.PluginOption}\"");
|
k = server.Password,
|
||||||
|
u = true,
|
||||||
|
plugin = server.Plugin,
|
||||||
|
plugin_opts = server.PluginOption
|
||||||
|
};
|
||||||
|
|
||||||
if (mode.BypassChina)
|
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()
|
public override void Stop()
|
||||||
{
|
{
|
||||||
StopInstance();
|
StopInstance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ namespace Netch.Servers.Shadowsocks
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 密码
|
/// 密码
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Password { get; set; }
|
public string Password { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 插件
|
/// 插件
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Netch.Forms;
|
using Netch.Forms;
|
||||||
|
using Netch.Utils;
|
||||||
|
|
||||||
namespace Netch.Servers.ShadowsocksR.Form
|
namespace Netch.Servers.ShadowsocksR.Form
|
||||||
{
|
{
|
||||||
@@ -8,7 +9,7 @@ namespace Netch.Servers.ShadowsocksR.Form
|
|||||||
{
|
{
|
||||||
server ??= new ShadowsocksR();
|
server ??= new ShadowsocksR();
|
||||||
Server = server;
|
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("EncryptMethod", "Encrypt Method", SSRGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod);
|
||||||
CreateComboBox("Protocol", "Protocol", SSRGlobal.Protocols, s => server.Protocol = s, server.Protocol);
|
CreateComboBox("Protocol", "Protocol", SSRGlobal.Protocols, s => server.Protocol = s, server.Protocol);
|
||||||
CreateTextBox("ProtocolParam", "Protocol Param", s => true, s => server.ProtocolParam = s, server.ProtocolParam);
|
CreateTextBox("ProtocolParam", "Protocol Param", s => true, s => server.ProtocolParam = s, server.ProtocolParam);
|
||||||
|
|||||||
@@ -24,31 +24,64 @@ namespace Netch.Servers.ShadowsocksR
|
|||||||
{
|
{
|
||||||
var server = (ShadowsocksR) s;
|
var server = (ShadowsocksR) s;
|
||||||
|
|
||||||
#region Argument
|
var command = new SSRParameter
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
argument.Append($" -O {server.Protocol}");
|
s = server.AutoResolveHostname(),
|
||||||
if (!string.IsNullOrEmpty(server.ProtocolParam))
|
p = server.Port,
|
||||||
argument.Append($" -G \"{server.ProtocolParam}\"");
|
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)
|
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()
|
public override void Stop()
|
||||||
|
|||||||
@@ -7,26 +7,16 @@ namespace Netch.Servers.ShadowsocksR
|
|||||||
{
|
{
|
||||||
public override string Type { get; } = "SSR";
|
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>
|
||||||
/// 密码
|
/// 密码
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Password { get; set; } = string.Empty;
|
public string Password { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加密方式
|
||||||
|
/// </summary>
|
||||||
|
public string EncryptMethod { get; set; } = SSRGlobal.EncryptMethods[0];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 协议
|
/// 协议
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -36,6 +26,16 @@ namespace Netch.Servers.ShadowsocksR
|
|||||||
/// 协议参数
|
/// 协议参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? ProtocolParam { get; set; }
|
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
|
public class SSRGlobal
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 额外 ID
|
/// 额外 ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string aid { get; set; } = string.Empty;
|
public int aid { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 伪装域名(HTTP,WS)
|
/// 伪装域名(HTTP,WS)
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 端口
|
/// 端口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string port { get; set; } = string.Empty;
|
public ushort port { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 备注
|
/// 备注
|
||||||
@@ -58,6 +58,6 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 链接版本
|
/// 链接版本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string v { get; set; } = string.Empty;
|
public int v { get; set; } = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,9 +77,7 @@ namespace Netch.Servers.V2ray
|
|||||||
var parameter = new Dictionary<string, string>();
|
var parameter = new Dictionary<string, string>();
|
||||||
// protocol-specific fields
|
// protocol-specific fields
|
||||||
parameter.Add("type", server.TransferProtocol);
|
parameter.Add("type", server.TransferProtocol);
|
||||||
if (server.EncryptMethod == "none")
|
parameter.Add("encryption", server.EncryptMethod);
|
||||||
// VLESS outbounds[].settings.encryption,当前可选值只有 none
|
|
||||||
parameter.Add("encryption", server.EncryptMethod);
|
|
||||||
|
|
||||||
// transport-specific fields
|
// transport-specific fields
|
||||||
switch (server.TransferProtocol)
|
switch (server.TransferProtocol)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using Netch.Controllers;
|
using Netch.Controllers;
|
||||||
using Netch.Models;
|
using Netch.Models;
|
||||||
using Netch.Servers.V2ray;
|
using Netch.Servers.V2ray;
|
||||||
@@ -43,12 +44,12 @@ namespace Netch.Servers.VMess
|
|||||||
|
|
||||||
var vmessJson = JsonSerializer.Serialize(new V2rayNSharing
|
var vmessJson = JsonSerializer.Serialize(new V2rayNSharing
|
||||||
{
|
{
|
||||||
v = "2",
|
v = 2,
|
||||||
ps = server.Remark,
|
ps = server.Remark,
|
||||||
add = server.Hostname,
|
add = server.Hostname,
|
||||||
port = server.Port.ToString(),
|
port = server.Port,
|
||||||
id = server.UserID,
|
id = server.UserID,
|
||||||
aid = server.AlterID.ToString(),
|
aid = server.AlterID,
|
||||||
net = server.TransferProtocol,
|
net = server.TransferProtocol,
|
||||||
type = server.FakeType,
|
type = server.FakeType,
|
||||||
host = server.Host,
|
host = server.Host,
|
||||||
@@ -85,13 +86,14 @@ namespace Netch.Servers.VMess
|
|||||||
return V2rayUtils.ParseVUri(text);
|
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.Remark = vmess.ps;
|
||||||
data.Hostname = vmess.add;
|
data.Hostname = vmess.add;
|
||||||
data.Port = ushort.Parse(vmess.port);
|
data.Port = vmess.port;
|
||||||
data.UserID = vmess.id;
|
data.UserID = vmess.id;
|
||||||
data.AlterID = int.Parse(vmess.aid);
|
data.AlterID = vmess.aid;
|
||||||
data.TransferProtocol = vmess.net;
|
data.TransferProtocol = vmess.net;
|
||||||
data.FakeType = vmess.type;
|
data.FakeType = vmess.type;
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,17 @@ namespace Netch.Updater
|
|||||||
|
|
||||||
private void ApplyUpdate()
|
private void ApplyUpdate()
|
||||||
{
|
{
|
||||||
|
// Pre Update
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Backup Configuration file
|
||||||
|
File.Copy(Configuration.SettingFileFullName, Configuration.SettingFileFullName + ".bak", true);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
// extract Update file to {tempDirectory}\extract
|
// extract Update file to {tempDirectory}\extract
|
||||||
var extractPath = Path.Combine(_tempDirectory, "extract");
|
var extractPath = Path.Combine(_tempDirectory, "extract");
|
||||||
int exitCode;
|
int exitCode;
|
||||||
@@ -126,7 +137,7 @@ namespace Netch.Updater
|
|||||||
if (extendedKeepDirectories.Any(p => file.StartsWith(p)))
|
if (extendedKeepDirectories.Any(p => file.StartsWith(p)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (Path.GetFileName(file) is ModeHelper.DISABLE_MODE_DIRECTORY_FILENAME)
|
if (Path.GetFileName(file) is ModeHelper.DisableModeDirectoryFileName)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
filesToDelete.Add(file);
|
filesToDelete.Add(file);
|
||||||
|
|||||||
@@ -12,12 +12,10 @@ namespace Netch.Utils
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据目录
|
/// 数据目录
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string DATA_DIR = "data";
|
public static string DataDirectoryFullName => Path.Combine(Global.NetchDir, "data");
|
||||||
|
|
||||||
|
public static string SettingFileFullName => $"{DataDirectoryFullName}\\settings.json";
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置
|
|
||||||
/// </summary>
|
|
||||||
public static readonly string SETTINGS_JSON = $"{DATA_DIR}\\settings.json";
|
|
||||||
private static readonly JsonSerializerOptions JsonSerializerOptions = Global.NewDefaultJsonSerializerOptions;
|
private static readonly JsonSerializerOptions JsonSerializerOptions = Global.NewDefaultJsonSerializerOptions;
|
||||||
|
|
||||||
static Configuration()
|
static Configuration()
|
||||||
@@ -31,45 +29,39 @@ namespace Netch.Utils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static void Load()
|
public static void Load()
|
||||||
{
|
{
|
||||||
if (File.Exists(SETTINGS_JSON))
|
if (File.Exists(SettingFileFullName))
|
||||||
{
|
{
|
||||||
Global.Settings = ParseSetting(File.ReadAllText(SETTINGS_JSON));
|
try
|
||||||
|
{
|
||||||
|
using var fileStream = File.OpenRead(SettingFileFullName);
|
||||||
|
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
|
else
|
||||||
{
|
{
|
||||||
// 弹出提示
|
// 保存默认设置
|
||||||
i18N.Load("System");
|
|
||||||
|
|
||||||
// 创建 data 文件夹并保存默认设置
|
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Setting ParseSetting(string text)
|
private static void CheckSetting(Setting settings)
|
||||||
{
|
{
|
||||||
try
|
settings.Profiles.RemoveAll(p => p.ServerRemark == string.Empty || p.ModeRemark == string.Empty);
|
||||||
{
|
|
||||||
var settings = JsonSerializer.Deserialize<Setting>(text, JsonSerializerOptions)!;
|
|
||||||
|
|
||||||
#region Check Profile
|
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.RemoveAll(p => p.ServerRemark == string.Empty || p.ModeRemark == string.Empty);
|
settings.Profiles[i].Index = i;
|
||||||
|
|
||||||
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!;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -77,10 +69,10 @@ namespace Netch.Utils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static void Save()
|
public static void Save()
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(DATA_DIR))
|
if (!Directory.Exists(DataDirectoryFullName))
|
||||||
Directory.CreateDirectory(DATA_DIR);
|
Directory.CreateDirectory(DataDirectoryFullName);
|
||||||
|
|
||||||
File.WriteAllBytes(SETTINGS_JSON, JsonSerializer.SerializeToUtf8Bytes(Global.Settings, JsonSerializerOptions));
|
File.WriteAllBytes(SettingFileFullName, JsonSerializer.SerializeToUtf8Bytes(Global.Settings, JsonSerializerOptions));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,14 +40,23 @@ namespace Netch.Utils
|
|||||||
private static void Write(string text, LogLevel logLevel)
|
private static void Write(string text, LogLevel logLevel)
|
||||||
{
|
{
|
||||||
var contents = $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Global.EOF}";
|
var contents = $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Global.EOF}";
|
||||||
if (Global.Testing)
|
#if DEBUG
|
||||||
|
switch (logLevel)
|
||||||
{
|
{
|
||||||
Console.WriteLine(contents);
|
case LogLevel.INFO:
|
||||||
return;
|
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)
|
lock (FileLock)
|
||||||
File.AppendAllText(LogFile, contents);
|
File.AppendAllText(LogFile, contents);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,19 +10,45 @@ namespace Netch.Utils
|
|||||||
{
|
{
|
||||||
public static class ModeHelper
|
public static class ModeHelper
|
||||||
{
|
{
|
||||||
private const string MODE_DIR = "mode";
|
public const string DisableModeDirectoryFileName = "disabled";
|
||||||
public const string DISABLE_MODE_DIRECTORY_FILENAME = "disabled";
|
|
||||||
|
|
||||||
public static readonly string ModeDirectory = Path.Combine(Global.NetchDir, $"{MODE_DIR}\\");
|
public static string ModeDirectoryFullName => Path.Combine(Global.NetchDir, "mode");
|
||||||
|
|
||||||
|
private static readonly FileSystemWatcher FileSystemWatcher;
|
||||||
|
|
||||||
|
static ModeHelper()
|
||||||
|
{
|
||||||
|
FileSystemWatcher = new FileSystemWatcher(ModeDirectoryFullName)
|
||||||
|
{
|
||||||
|
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)
|
public static string GetRelativePath(string fullName)
|
||||||
{
|
{
|
||||||
return fullName.Substring(ModeDirectory.Length);
|
var length = ModeDirectoryFullName.Length;
|
||||||
|
if (!ModeDirectoryFullName.EndsWith("\\"))
|
||||||
|
length++;
|
||||||
|
|
||||||
|
return fullName.Substring(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetFullPath(string relativeName)
|
public static string GetFullPath(string relativeName)
|
||||||
{
|
{
|
||||||
return Path.Combine(ModeDirectory, relativeName);
|
return Path.Combine(ModeDirectoryFullName, relativeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -31,7 +57,7 @@ namespace Netch.Utils
|
|||||||
public static void Load()
|
public static void Load()
|
||||||
{
|
{
|
||||||
Global.Modes.Clear();
|
Global.Modes.Clear();
|
||||||
LoadModeDirectory(ModeDirectory);
|
LoadModeDirectory(ModeDirectoryFullName);
|
||||||
|
|
||||||
Sort();
|
Sort();
|
||||||
}
|
}
|
||||||
@@ -44,11 +70,18 @@ namespace Netch.Utils
|
|||||||
LoadModeDirectory(directory);
|
LoadModeDirectory(directory);
|
||||||
|
|
||||||
// skip Directory with a disabled file in
|
// skip Directory with a disabled file in
|
||||||
if (File.Exists(Path.Combine(modeDirectory, DISABLE_MODE_DIRECTORY_FILENAME)))
|
if (File.Exists(Path.Combine(modeDirectory, DisableModeDirectoryFileName)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var file in Directory.GetFiles(modeDirectory).Where(f => f.EndsWith(".txt")))
|
foreach (var file in Directory.GetFiles(modeDirectory).Where(f => f.EndsWith(".txt")))
|
||||||
LoadModeFile(file);
|
try
|
||||||
|
{
|
||||||
|
Global.Modes.Add(new Mode(file));
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -56,71 +89,17 @@ namespace Netch.Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LoadModeFile(string fullName)
|
|
||||||
{
|
|
||||||
var mode = new Mode(fullName);
|
|
||||||
|
|
||||||
var content = File.ReadAllLines(fullName);
|
|
||||||
if (content.Length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (var i = 0; i < content.Length; i++)
|
|
||||||
{
|
|
||||||
var text = content[i].Trim();
|
|
||||||
|
|
||||||
if (i == 0)
|
|
||||||
{
|
|
||||||
if (text.First() != '#')
|
|
||||||
return;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mode.Rule.Add(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Global.Modes.Add(mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Sort()
|
private static void Sort()
|
||||||
{
|
{
|
||||||
Global.Modes.Sort((a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
|
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)
|
public static void Delete(Mode mode)
|
||||||
{
|
{
|
||||||
if (mode.FullName == null)
|
if (mode.FullName == null)
|
||||||
throw new ArgumentException("FullName");
|
throw new ArgumentException(nameof(mode.FullName));
|
||||||
|
|
||||||
if (File.Exists(mode.FullName))
|
File.Delete(mode.FullName);
|
||||||
File.Delete(mode.FullName);
|
|
||||||
|
|
||||||
Global.Modes.Remove(mode);
|
|
||||||
Global.MainForm.LoadModes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool SkipServerController(Server server, Mode mode)
|
public static bool SkipServerController(Server server, Mode mode)
|
||||||
@@ -137,17 +116,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;
|
port = null;
|
||||||
portName = string.Empty;
|
portName = string.Empty;
|
||||||
portType = PortType.Both;
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
port = Global.Settings.RedirectorTCPPort;
|
port = Global.Settings.RedirectorTCPPort;
|
||||||
portName = "Redirector TCP";
|
portName = "Redirector TCP";
|
||||||
portType = PortType.TCP;
|
|
||||||
return new NFController();
|
return new NFController();
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
@@ -156,7 +133,6 @@ namespace Netch.Utils
|
|||||||
case 5:
|
case 5:
|
||||||
port = Global.Settings.HTTPLocalPort;
|
port = Global.Settings.HTTPLocalPort;
|
||||||
portName = "HTTP";
|
portName = "HTTP";
|
||||||
portType = PortType.TCP;
|
|
||||||
StatusPortInfoText.HttpPort = (ushort) port;
|
StatusPortInfoText.HttpPort = (ushort) port;
|
||||||
return new HTTPController();
|
return new HTTPController();
|
||||||
case 4:
|
case 4:
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
using Netch.Models;
|
using Netch.Models;
|
||||||
|
using static Vanara.PInvoke.IpHlpApi;
|
||||||
|
using static Vanara.PInvoke.Ws2_32;
|
||||||
|
|
||||||
namespace Netch.Utils
|
namespace Netch.Utils
|
||||||
{
|
{
|
||||||
@@ -26,6 +28,16 @@ namespace Netch.Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Process> GetProcessByUsedTcpPort(ushort port)
|
||||||
|
{
|
||||||
|
if (port == 0)
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
var row = GetTcpTable2().Where(r => ntohs((ushort) r.dwLocalPort) == port);
|
||||||
|
|
||||||
|
return row.Select(r => Process.GetProcessById((int) r.dwOwningPid));
|
||||||
|
}
|
||||||
|
|
||||||
private static void GetReservedPortRange(PortType portType, ref List<Range> targetList)
|
private static void GetReservedPortRange(PortType portType, ref List<Range> targetList)
|
||||||
{
|
{
|
||||||
var process = new Process
|
var process = new Process
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace Netch.Utils
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
// Unsupported Server Type
|
||||||
return JsonSerializer.Deserialize<Server>(jsonElement.GetRawText(), new JsonSerializerOptions())!;
|
return JsonSerializer.Deserialize<Server>(jsonElement.GetRawText(), new JsonSerializerOptions())!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ namespace Netch.Utils
|
|||||||
{
|
{
|
||||||
public static bool Open(string path)
|
public static bool Open(string path)
|
||||||
{
|
{
|
||||||
if (Global.Testing)
|
|
||||||
return true;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Process.Start(new ProcessStartInfo
|
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)
|
public static string GetFileVersion(string file)
|
||||||
{
|
{
|
||||||
return File.Exists(file) ? FileVersionInfo.GetVersionInfo(file).FileVersion : string.Empty;
|
return File.Exists(file) ? FileVersionInfo.GetVersionInfo(file).FileVersion : string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void DrawCenterComboBox(object sender, DrawItemEventArgs e)
|
public static void DrawCenterComboBox(object sender, DrawItemEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is ComboBox cbx)
|
if (sender is ComboBox cbx)
|
||||||
|
|||||||
3
SearchComboBox/.gitignore
vendored
3
SearchComboBox/.gitignore
vendored
@@ -1,3 +0,0 @@
|
|||||||
/bin
|
|
||||||
/obj
|
|
||||||
/SearchComboBox.csproj.user
|
|
||||||
3
UnitTest/.gitignore
vendored
3
UnitTest/.gitignore
vendored
@@ -1,3 +0,0 @@
|
|||||||
/bin
|
|
||||||
/obj
|
|
||||||
/Netch.csproj.user
|
|
||||||
@@ -6,10 +6,10 @@ using Netch.Utils;
|
|||||||
namespace UnitTest
|
namespace UnitTest
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class FunctionTest : TestBase
|
public class Function : TestBase
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestLoadI18N()
|
public void LoadLanguage()
|
||||||
{
|
{
|
||||||
void TestLoad(string t)
|
void TestLoad(string t)
|
||||||
{
|
{
|
||||||
98
UnitTest/ParameterTest.cs
Normal file
98
UnitTest/ParameterTest.cs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,10 +10,10 @@ using Netch.Utils;
|
|||||||
namespace UnitTest
|
namespace UnitTest
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class TestParseShareLink : TestBase
|
public class ParseShareLink : TestBase
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestServerFromSSR()
|
public void ParseSSR()
|
||||||
{
|
{
|
||||||
const string normalCase =
|
const string normalCase =
|
||||||
"ssr://MTI3LjAuMC4xOjEyMzQ6YXV0aF9hZXMxMjhfbWQ1OmFlcy0xMjgtY2ZiOnRsczEuMl90aWNrZXRfYXV0aDpZV0ZoWW1KaS8_b2Jmc3BhcmFtPVluSmxZV3QzWVRFeExtMXZaUQ";
|
"ssr://MTI3LjAuMC4xOjEyMzQ6YXV0aF9hZXMxMjhfbWQ1OmFlcy0xMjgtY2ZiOnRsczEuMl90aWNrZXRfYXV0aDpZV0ZoWW1KaS8_b2Jmc3BhcmFtPVluSmxZV3QzWVRFeExtMXZaUQ";
|
||||||
|
|||||||
26
UnitTest/SuffixVersionTest.cs
Normal file
26
UnitTest/SuffixVersionTest.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Netch.Models.GitHubRelease;
|
||||||
|
|
||||||
|
namespace UnitTest
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class SuffixVersionTest
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public void Test()
|
||||||
|
{
|
||||||
|
var rel = SuffixVersion.Parse("1.0.0");
|
||||||
|
var a1 = SuffixVersion.Parse("1.0.0-Alpha1");
|
||||||
|
var a3 = SuffixVersion.Parse("1.0.0-aLpHa3");
|
||||||
|
var b2 = SuffixVersion.Parse("1.0.0-betA2");
|
||||||
|
|
||||||
|
Assert.AreEqual(rel.ToString(), "1.0.0");
|
||||||
|
Assert.AreEqual(a1.ToString(), "1.0.0-Alpha1");
|
||||||
|
Assert.IsTrue(rel.CompareTo(a1) > 0);
|
||||||
|
Assert.IsTrue(rel.CompareTo(b2) > 0);
|
||||||
|
|
||||||
|
Assert.IsTrue(b2.CompareTo(a1) > 0);
|
||||||
|
Assert.IsTrue(b2.CompareTo(a3) > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,6 @@
|
|||||||
using Netch;
|
namespace UnitTest
|
||||||
|
|
||||||
namespace UnitTest
|
|
||||||
{
|
{
|
||||||
public class TestBase
|
public class TestBase
|
||||||
{
|
{
|
||||||
protected TestBase()
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
Global.Testing = true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -23,8 +24,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.1" />
|
<PackageReference Include="MSTest.TestAdapter" Version="2.2.3" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.1" />
|
<PackageReference Include="MSTest.TestFramework" Version="2.2.3" />
|
||||||
<PackageReference Include="System.Text.Json" Version="5.0.1" />
|
<PackageReference Include="System.Text.Json" Version="5.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
2
binaries
2
binaries
Submodule binaries updated: 61b435e28c...c64a784152
2
modes
2
modes
Submodule modes updated: 42375ef724...72ad96d018
Reference in New Issue
Block a user