mirror of
https://github.com/netchx/netch.git
synced 2026-05-11 23:45:06 +08:00
Compare commits
52 Commits
1.4.12-Bet
...
1.4.12
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26cde4a8bb | ||
|
|
e5e7892741 | ||
|
|
764009ebff | ||
|
|
0b09ce62f1 | ||
|
|
3d24aa7583 | ||
|
|
689f29d0f0 | ||
|
|
715d25a6f6 | ||
|
|
0d1762eebe | ||
|
|
8f7f2e3d1c | ||
|
|
3311115bda | ||
|
|
d447f963df | ||
|
|
0e2d3b63b7 | ||
|
|
6d85c78552 | ||
|
|
3883239836 | ||
|
|
2e0655a2a8 | ||
|
|
11be70d352 | ||
|
|
46dfc885a2 | ||
|
|
5495b94513 | ||
|
|
4e5bdab6b8 | ||
|
|
f61827a575 | ||
|
|
87adf4de1e | ||
|
|
3488e7bbf3 | ||
|
|
8c9953a5d4 | ||
|
|
aef6be2c80 | ||
|
|
c9396bd6b2 | ||
|
|
b6a3f5bc36 | ||
|
|
bbf9c5ed7c | ||
|
|
6174e78696 | ||
|
|
75045fe1a6 | ||
|
|
3da7451b9f | ||
|
|
27d68163a9 | ||
|
|
b1c0990ca5 | ||
|
|
7181db0376 | ||
|
|
7a15fa7375 | ||
|
|
e10c994e38 | ||
|
|
54a263ad06 | ||
|
|
8a56dd4582 | ||
|
|
cd891cec33 | ||
|
|
635a033434 | ||
|
|
6fd3aa48a5 | ||
|
|
a319833bd5 | ||
|
|
70e5d8324e | ||
|
|
1cecccb173 | ||
|
|
281c67aced | ||
|
|
a779295525 | ||
|
|
e665850fc9 | ||
|
|
2537fdd8fe | ||
|
|
cc459d3f59 | ||
|
|
7c051413d3 | ||
|
|
17165e4623 | ||
|
|
a6fd0764e1 | ||
|
|
d27c7c016c |
34
.github/workflows/build.yml
vendored
34
.github/workflows/build.yml
vendored
@@ -5,10 +5,10 @@ jobs:
|
||||
name: Build
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: NuGet
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v1
|
||||
|
||||
- name: MSBuild
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v1.0.0
|
||||
|
||||
- name: Checkout
|
||||
@@ -17,6 +17,14 @@ jobs:
|
||||
- name: Update submodules
|
||||
uses: snickerbockers/submodules-init@v4
|
||||
|
||||
- name: Restore NuGet Packages Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('Netch/Netch.csproj') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-nuget-
|
||||
|
||||
- name: Restore NuGet Package
|
||||
run: nuget restore Netch.sln
|
||||
|
||||
@@ -27,13 +35,14 @@ jobs:
|
||||
New-Item -ItemType Directory -Path C:\builtfiles -Force > $null
|
||||
Compress-Archive -Path Netch\bin\x64\Release\win-x64\* -DestinationPath C:\builtfiles\Netch.zip
|
||||
echo "::set-env name=Netch_SHA256::$(.\GetSHA256.ps1 C:\builtfiles\Netch.zip)"
|
||||
echo "::set-env name=Netch_EXE_SHA256::$(.\GetSHA256.ps1 Netch\bin\x64\Release\win-x64\Netch.exe)"
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Netch
|
||||
path: Netch\bin\x64\Release\win-x64
|
||||
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
@@ -55,3 +64,22 @@ jobs:
|
||||
| 文件名 | SHA256 |
|
||||
| :- | :- |
|
||||
| Netch.zip | ${{ env.Netch_SHA256 }} |
|
||||
|
||||
# Deploy:
|
||||
# needs: [build]
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Download Artifacts
|
||||
# uses: actions/download-artifact@v1
|
||||
# with:
|
||||
# name: Netch
|
||||
|
||||
# - name: Pushes to another repository
|
||||
# uses: peaceiris/actions-gh-pages@v3
|
||||
# with:
|
||||
# personal_token: ${{ secrets.ACCESS_TOKEN }}
|
||||
# publish_branch: repo
|
||||
# publish_dir: ./Netch
|
||||
# external_repository: ${{ github.repository_owner }}/NetchReleases
|
||||
# user_name: 'github-actions[bot]'
|
||||
# user_email: 'github-actions[bot]@users.noreply.github.com'
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Controllers
|
||||
@@ -8,10 +7,9 @@ namespace Netch.Controllers
|
||||
{
|
||||
public DNSController()
|
||||
{
|
||||
AkaName = "dns Service";
|
||||
MainFile = "unbound";
|
||||
ExtFiles = new[] {"unbound-service.conf", "forward-zone.conf"};
|
||||
InitCheck();
|
||||
Name = "DNS Service";
|
||||
MainFile = "unbound.exe";
|
||||
RedirectStd = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -20,31 +18,7 @@ namespace Netch.Controllers
|
||||
/// <returns></returns>
|
||||
public bool Start()
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
Instance = GetProcess("bin\\unbound.exe");
|
||||
Instance.StartInfo.Arguments = "-c unbound-service.conf -v";
|
||||
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
|
||||
try
|
||||
{
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error("dns-unbound 进程出错:\n" + e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
WriteLog(e);
|
||||
return StartInstanceAuto("-c unbound-service.conf -v");
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
@@ -10,14 +9,16 @@ namespace Netch.Controllers
|
||||
{
|
||||
public SSController()
|
||||
{
|
||||
MainFile = "Shadowsocks";
|
||||
InitCheck();
|
||||
Name = "Shadowsocks";
|
||||
MainFile = "Shadowsocks.exe";
|
||||
StartedKeywords.Add("listening at");
|
||||
StoppedKeywords.AddRange(new[] {"Invalid config path", "usage", "plugin service exit unexpectedly"});
|
||||
}
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
//从DLL启动Shaowsocks
|
||||
if (Global.Settings.BootShadowsocksFromDLL && (mode.Type == 0 || mode.Type == 1 || mode.Type == 2 || mode.Type == 3))
|
||||
if (Global.Settings.BootShadowsocksFromDLL && (mode.Type == 0 || mode.Type == 1 || mode.Type == 2))
|
||||
{
|
||||
State = State.Starting;
|
||||
var client = Encoding.UTF8.GetBytes($"0.0.0.0:{Global.Settings.Socks5LocalPort}");
|
||||
@@ -45,39 +46,17 @@ namespace Netch.Controllers
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Ready) return false;
|
||||
Instance = GetProcess("bin\\Shadowsocks.exe");
|
||||
#region Argument
|
||||
|
||||
Instance.StartInfo.Arguments = $"-s {server.Hostname} -p {server.Port} -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u";
|
||||
var argument = new StringBuilder();
|
||||
argument.Append($"-s {server.Hostname} -p {server.Port} -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u");
|
||||
if (!string.IsNullOrWhiteSpace(server.Plugin) && !string.IsNullOrWhiteSpace(server.PluginOption))
|
||||
Instance.StartInfo.Arguments += $" --plugin {server.Plugin} --plugin-opts \"{server.PluginOption}\"";
|
||||
if (mode.BypassChina) Instance.StartInfo.Arguments += " --acl default.acl";
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
argument.Append($" --plugin {server.Plugin} --plugin-opts \"{server.PluginOption}\"");
|
||||
if (mode.BypassChina) argument.Append(" --acl default.acl");
|
||||
|
||||
State = State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
#endregion
|
||||
|
||||
for (var i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
|
||||
if (State == State.Started) return true;
|
||||
|
||||
if (State == State.Stopped)
|
||||
{
|
||||
Logging.Error("SS 进程启动失败");
|
||||
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Error("SS 进程启动超时");
|
||||
Stop();
|
||||
return false;
|
||||
return StartInstanceAuto(argument.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -89,18 +68,5 @@ namespace Netch.Controllers
|
||||
else
|
||||
StopInstance();
|
||||
}
|
||||
|
||||
public override void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
State = State.Stopped;
|
||||
else if (e.Data.Contains("listening at"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("Invalid config path") || e.Data.Contains("usage") || e.Data.Contains("plugin service exit unexpectedly")) State = State.Stopped;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
@@ -9,75 +9,36 @@ namespace Netch.Controllers
|
||||
{
|
||||
public SSRController()
|
||||
{
|
||||
MainFile = "ShadowsocksR";
|
||||
InitCheck();
|
||||
Name = "ShadowsocksR";
|
||||
MainFile = "ShadowsocksR.exe";
|
||||
StartedKeywords.Add("listening at");
|
||||
StoppedKeywords.AddRange(new[] {"Invalid config path", "usage"});
|
||||
}
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
Instance = GetProcess("bin\\ShadowsocksR.exe");
|
||||
Instance.StartInfo.FileName = "bin\\ShadowsocksR.exe";
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
|
||||
Instance.StartInfo.Arguments = $"-s {server.Hostname} -p {server.Port} -k \"{server.Password}\" -m {server.EncryptMethod} -t 120";
|
||||
#region Argument
|
||||
|
||||
var argument = new StringBuilder();
|
||||
argument.Append($"-s {server.Hostname} -p {server.Port} -k \"{server.Password}\" -m {server.EncryptMethod} -t 120");
|
||||
if (!string.IsNullOrEmpty(server.Protocol))
|
||||
{
|
||||
Instance.StartInfo.Arguments += $" -O {server.Protocol}";
|
||||
|
||||
if (!string.IsNullOrEmpty(server.ProtocolParam)) Instance.StartInfo.Arguments += $" -G \"{server.ProtocolParam}\"";
|
||||
argument.Append($" -O {server.Protocol}");
|
||||
if (!string.IsNullOrEmpty(server.ProtocolParam)) argument.Append($" -G \"{server.ProtocolParam}\"");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(server.OBFS))
|
||||
{
|
||||
Instance.StartInfo.Arguments += $" -o {server.OBFS}";
|
||||
|
||||
if (!string.IsNullOrEmpty(server.OBFSParam)) Instance.StartInfo.Arguments += $" -g \"{server.OBFSParam}\"";
|
||||
argument.Append($" -o {server.OBFS}");
|
||||
if (!string.IsNullOrEmpty(server.OBFSParam)) argument.Append($" -g \"{server.OBFSParam}\"");
|
||||
}
|
||||
|
||||
Instance.StartInfo.Arguments += $" -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -u";
|
||||
argument.Append($" -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -u");
|
||||
if (mode.BypassChina) argument.Append(" --acl default.acl");
|
||||
|
||||
if (mode.BypassChina) Instance.StartInfo.Arguments += " --acl default.acl";
|
||||
#endregion
|
||||
|
||||
State = State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
|
||||
for (var i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
|
||||
if (State == State.Started) return true;
|
||||
|
||||
if (State == State.Stopped)
|
||||
{
|
||||
Logging.Error("SSR 进程启动失败");
|
||||
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Error("SSR 进程启动超时");
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
State = State.Stopped;
|
||||
else if (e.Data.Contains("listening at"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("Invalid config path") || e.Data.Contains("usage")) State = State.Stopped;
|
||||
}
|
||||
return StartInstanceAuto(argument.ToString());
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
@@ -12,14 +12,14 @@ namespace Netch.Controllers
|
||||
{
|
||||
public TrojanController()
|
||||
{
|
||||
MainFile = "Trojan";
|
||||
InitCheck();
|
||||
Name = "Trojan";
|
||||
MainFile = "Trojan.exe";
|
||||
StartedKeywords.Add("started");
|
||||
StoppedKeywords.Add("exiting");
|
||||
}
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new Trojan
|
||||
{
|
||||
local_addr = Global.Settings.LocalAddress,
|
||||
@@ -32,46 +32,7 @@ namespace Netch.Controllers
|
||||
}
|
||||
}));
|
||||
|
||||
Instance = GetProcess("bin\\Trojan.exe");
|
||||
Instance.StartInfo.Arguments = "-c ..\\data\\last.json";
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
|
||||
State = State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
for (var i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
|
||||
if (State == State.Started) return true;
|
||||
|
||||
if (State == State.Stopped)
|
||||
{
|
||||
Logging.Error("Trojan 进程启动失败");
|
||||
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Error("Trojan 进程启动超时");
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
State = State.Stopped;
|
||||
else if (e.Data.Contains("started"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("exiting")) State = State.Stopped;
|
||||
}
|
||||
return StartInstanceAuto("-c ..\\data\\last.json");
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Netch.Models;
|
||||
@@ -13,13 +12,14 @@ namespace Netch.Controllers
|
||||
{
|
||||
public VMessController()
|
||||
{
|
||||
MainFile = "v2ray";
|
||||
InitCheck();
|
||||
Name = "V2Ray";
|
||||
MainFile = "v2ray.exe";
|
||||
StartedKeywords.Add("started");
|
||||
StoppedKeywords.AddRange(new[] {"config file not readable", "failed to"});
|
||||
}
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new VMess.Config
|
||||
{
|
||||
inbounds = new List<VMess.Inbounds>
|
||||
@@ -58,14 +58,14 @@ namespace Netch.Controllers
|
||||
streamSettings = new VMess.StreamSettings
|
||||
{
|
||||
network = server.TransferProtocol,
|
||||
security = server.TLSSecure ? "tls" : "",
|
||||
security = server.TLSSecure ? "tls" : string.Empty,
|
||||
wsSettings = server.TransferProtocol == "ws"
|
||||
? new VMess.WebSocketSettings
|
||||
{
|
||||
path = server.Path == "" ? "/" : server.Path,
|
||||
path = server.Path == string.Empty ? "/" : server.Path,
|
||||
headers = new VMess.WSHeaders
|
||||
{
|
||||
Host = server.Host == "" ? server.Hostname : server.Host
|
||||
Host = server.Host == string.Empty ? server.Hostname : server.Host
|
||||
}
|
||||
}
|
||||
: null,
|
||||
@@ -77,10 +77,10 @@ namespace Netch.Controllers
|
||||
type = server.FakeType,
|
||||
request = new VMess.TCPRequest
|
||||
{
|
||||
path = server.Path == "" ? "/" : server.Path,
|
||||
path = server.Path == string.Empty ? "/" : server.Path,
|
||||
headers = new VMess.TCPRequestHeaders
|
||||
{
|
||||
Host = server.Host == "" ? server.Hostname : server.Host
|
||||
Host = server.Host == string.Empty ? server.Hostname : server.Host
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,54 +168,15 @@ namespace Netch.Controllers
|
||||
}
|
||||
}));
|
||||
|
||||
Instance = GetProcess("bin\\v2ray.exe");
|
||||
Instance.StartInfo.Arguments = "-config ..\\data\\last.json";
|
||||
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
|
||||
State = State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
for (var i = 0; i < 1000; i++)
|
||||
if (StartInstanceAuto("-config ..\\data\\last.json"))
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
|
||||
if (State == State.Started)
|
||||
{
|
||||
if (File.Exists("data\\last.json")) File.Delete("data\\last.json");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (State == State.Stopped)
|
||||
{
|
||||
Logging.Error("V2Ray 进程启动失败");
|
||||
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
if (File.Exists("data\\last.json")) File.Delete("data\\last.json");
|
||||
return true;
|
||||
}
|
||||
|
||||
Logging.Error("V2Ray 进程启动超时");
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
State = State.Stopped;
|
||||
else if (e.Data.Contains("started"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("config file not readable") || e.Data.Contains("failed to")) State = State.Stopped;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
|
||||
@@ -1,150 +1,22 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public abstract class Controller
|
||||
public abstract partial class Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// 控制器名
|
||||
/// <param />
|
||||
/// 未赋值会在 <see cref="InitCheck" /> 赋值为 <see cref="MainFile" />
|
||||
/// </summary>
|
||||
public string AkaName;
|
||||
|
||||
/// <summary>
|
||||
/// 其他需要文件
|
||||
/// </summary>
|
||||
protected string[] ExtFiles;
|
||||
|
||||
/// <summary>
|
||||
/// 进程实例
|
||||
/// </summary>
|
||||
public Process Instance;
|
||||
|
||||
/// <summary>
|
||||
/// 主程序名(不含扩展名)
|
||||
/// </summary>
|
||||
public string MainFile;
|
||||
|
||||
/// <summary>
|
||||
/// 运行检查, 由 <see cref="InitCheck()" /> 赋值
|
||||
/// </summary>
|
||||
public bool Ready;
|
||||
public string Name { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前状态
|
||||
/// </summary>
|
||||
protected State State = State.Waiting;
|
||||
|
||||
public abstract void Stop();
|
||||
public State State { get; protected set; } = State.Waiting;
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
protected void StopInstance()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Instance == null || Instance.HasExited) return;
|
||||
Instance.Kill();
|
||||
Instance.WaitForExit();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($"停止 {MainFile}.exe 错误:\n" + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 杀残留进程,清除日志,检查文件
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected void InitCheck()
|
||||
{
|
||||
if (string.IsNullOrEmpty(AkaName)) AkaName = MainFile;
|
||||
|
||||
var result = false;
|
||||
// 杀残留
|
||||
MainController.KillProcessByName(MainFile);
|
||||
// 清日志
|
||||
try
|
||||
{
|
||||
if (File.Exists($"logging\\{AkaName}.log")) File.Delete($"logging\\{AkaName}.log");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
// 检查文件
|
||||
var mainResult = true;
|
||||
var extResult = true;
|
||||
if (!string.IsNullOrEmpty(MainFile) && !File.Exists($"bin\\{MainFile}.exe"))
|
||||
{
|
||||
mainResult = false;
|
||||
Logging.Error($"主程序 bin\\{MainFile}.exe 不存在");
|
||||
}
|
||||
|
||||
if (ExtFiles == null)
|
||||
extResult = true;
|
||||
else
|
||||
foreach (var f in ExtFiles)
|
||||
if (!File.Exists($"bin\\{f}"))
|
||||
{
|
||||
extResult = false;
|
||||
Logging.Error($"附加文件 bin\\{f} 不存在");
|
||||
}
|
||||
|
||||
result = extResult && mainResult;
|
||||
if (!result)
|
||||
Logging.Error(AkaName + " 未就绪");
|
||||
Ready = result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写日志
|
||||
/// </summary>
|
||||
/// <param name="std"></param>
|
||||
/// <returns><see cref="std" />是否为空</returns>
|
||||
protected bool WriteLog(DataReceivedEventArgs std)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(std.Data)) return false;
|
||||
try
|
||||
|
||||
{
|
||||
File.AppendAllText($"logging\\{AkaName}.log", $@"{std.Data}{Global.EOF}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($"写入{AkaName}日志错误:\n" + e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Process GetProcess(string path = null, bool redirectStd = true)
|
||||
{
|
||||
var p = new Process
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
Arguments = "",
|
||||
WorkingDirectory = $"{Global.NetchDir}\\bin",
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardError = redirectStd,
|
||||
RedirectStandardInput = redirectStd,
|
||||
RedirectStandardOutput = redirectStd,
|
||||
UseShellExecute = false
|
||||
},
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
if (path != null) p.StartInfo.FileName = Path.GetFullPath(path);
|
||||
return p;
|
||||
}
|
||||
public abstract void Stop();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using Netch.Models;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
@@ -12,7 +11,5 @@ namespace Netch.Controllers
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public abstract bool Start(Server server, Mode mode);
|
||||
|
||||
public abstract void OnOutputDataReceived(object sender, DataReceivedEventArgs e);
|
||||
}
|
||||
}
|
||||
246
Netch/Controllers/Interface/Instance.cs
Normal file
246
Netch/Controllers/Interface/Instance.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
abstract partial class Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// 主程序名
|
||||
/// </summary>
|
||||
public string MainFile { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 成功启动关键词
|
||||
/// </summary>
|
||||
protected readonly List<string> StartedKeywords = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 启动失败关键词
|
||||
/// </summary>
|
||||
protected readonly List<string> StoppedKeywords = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 进程是否可以重定向输出
|
||||
/// </summary>
|
||||
protected bool RedirectStd { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 进程实例
|
||||
/// </summary>
|
||||
public Process Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 日志文件(重定向输出文件)
|
||||
/// </summary>
|
||||
private string _logPath;
|
||||
|
||||
private FileStream _logFileStream;
|
||||
|
||||
/// <summary>
|
||||
/// 程序输出的编码,
|
||||
/// 调用于基类的 <see cref="OnOutputDataReceived"/>
|
||||
/// </summary>
|
||||
protected string InstanceOutputEncoding { get; set; } = "gbk";
|
||||
|
||||
/// <summary>
|
||||
/// 停止进程
|
||||
/// </summary>
|
||||
protected void StopInstance()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Instance == null || Instance.HasExited) return;
|
||||
Instance.Kill();
|
||||
Instance.WaitForExit();
|
||||
}
|
||||
catch (Win32Exception e)
|
||||
{
|
||||
Logging.Error($"停止 {MainFile} 错误:\n" + e);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 仅初始化 <see cref="Instance"/>,不设定事件处理方法
|
||||
/// </summary>
|
||||
/// <param name="argument"></param>
|
||||
protected void InitInstance(string argument)
|
||||
{
|
||||
Instance = new Process
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
FileName = Path.GetFullPath($"bin\\{MainFile}"),
|
||||
WorkingDirectory = $"{Global.NetchDir}\\bin",
|
||||
Arguments = argument,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardError = RedirectStd,
|
||||
RedirectStandardInput = RedirectStd,
|
||||
RedirectStandardOutput = RedirectStd,
|
||||
UseShellExecute = !RedirectStd,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
},
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 默认行为启动主程序
|
||||
/// </summary>
|
||||
/// <param name="argument">主程序启动参数</param>
|
||||
/// <param name="priority">进程优先级</param>
|
||||
/// <returns>是否成功启动</returns>
|
||||
protected bool StartInstanceAuto(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal)
|
||||
{
|
||||
State = State.Starting;
|
||||
try
|
||||
{
|
||||
// 初始化程序
|
||||
InitInstance(argument);
|
||||
if (RedirectStd)
|
||||
{
|
||||
// 清理日志
|
||||
_logPath ??= Path.Combine(Global.NetchDir, $"logging\\{Name}.log");
|
||||
if (_logFileStream == null && File.Exists(_logPath))
|
||||
File.Delete(_logPath);
|
||||
_logFileStream = new FileStream(_logPath, FileMode.Create, FileAccess.Write);
|
||||
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
}
|
||||
|
||||
Instance.Exited += OnExited;
|
||||
|
||||
// 启动程序
|
||||
Instance.Start();
|
||||
if (priority != ProcessPriorityClass.Normal)
|
||||
Instance.PriorityClass = priority;
|
||||
if (!RedirectStd || StartedKeywords.Count == 0) return true;
|
||||
// 启动日志重定向
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
_writeStreamTimer.Elapsed += SaveStreamTimerEvent;
|
||||
_writeStreamTimer.Enabled = true;
|
||||
// 等待启动
|
||||
for (var i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
switch (State)
|
||||
{
|
||||
case State.Started:
|
||||
return true;
|
||||
case State.Stopped:
|
||||
Logging.Error($"{Name} 控制器启动失败");
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Error($"{Name} 控制器启动超时");
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($"{Name} 控制器启动失败:\n {e}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static System.Timers.Timer _writeStreamTimer = new System.Timers.Timer(300) {AutoReset = true};
|
||||
|
||||
private void OnExited(object sender, EventArgs e)
|
||||
{
|
||||
if (RedirectStd)
|
||||
{
|
||||
_writeStreamTimer.Enabled = false;
|
||||
Thread.Sleep(100); // 等待 Write() 写入流
|
||||
_logFileStream.Close();
|
||||
_logFileStream = null;
|
||||
}
|
||||
|
||||
State = State.Stopped;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 接收输出数据
|
||||
/// </summary>
|
||||
/// <param name="sender">发送者</param>
|
||||
/// <param name="e">数据</param>
|
||||
protected void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
// 程序结束, 接收到 null
|
||||
if (e.Data == null)
|
||||
return;
|
||||
|
||||
var info = Encoding.GetEncoding(InstanceOutputEncoding).GetBytes(e.Data + Global.EOF);
|
||||
Task.Run(() => Write(info));
|
||||
var str = Encoding.UTF8.GetString(info);
|
||||
// 检查启动
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (StartedKeywords.Any(s => str.Contains(s)))
|
||||
State = State.Started;
|
||||
else if (StoppedKeywords.Any(s => str.Contains(s)))
|
||||
State = State.Stopped;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计时器存储日志
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void SaveStreamTimerEvent(object sender, EventArgs e)
|
||||
{
|
||||
if (_logFileStream == null) return;
|
||||
try
|
||||
{
|
||||
await _logFileStream.FlushAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private readonly object _fileLocker = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 写入日志文件流
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <returns>转码后的字符串</returns>
|
||||
private void Write(byte[] info)
|
||||
{
|
||||
if (info == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
lock (_fileLocker)
|
||||
{
|
||||
_logFileStream.Write(info, 0, info.Length);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($"写入 {Name} 日志错误:\n" + e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Controllers
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
@@ -11,14 +14,55 @@ namespace Netch.Controllers
|
||||
{
|
||||
public class MainController
|
||||
{
|
||||
public EncryptedProxy pEncryptedProxyController;
|
||||
/// <summary>
|
||||
/// 记录当前使用的端口
|
||||
/// <see cref="MainForm.LocalPortText"/>
|
||||
/// </summary>
|
||||
public static readonly List<int> UsingPorts = new List<int>();
|
||||
|
||||
public ModeController pModeController;
|
||||
public EncryptedProxy pEncryptedProxyController { get; private set; }
|
||||
|
||||
public ModeController pModeController { get; private set; }
|
||||
|
||||
private Server _savedServer;
|
||||
private Mode _savedMode;
|
||||
|
||||
public string PortInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_savedMode == null || _savedServer == null)
|
||||
return string.Empty;
|
||||
|
||||
if (_savedServer.Type == "Socks5" && _savedMode.Type != 3 && _savedMode.Type != 5)
|
||||
// 不可控Socks5, 不可控HTTP
|
||||
return string.Empty;
|
||||
|
||||
var text = new StringBuilder();
|
||||
if (_localAddress == "0.0.0.0")
|
||||
text.Append(i18N.Translate("Allow other Devices to connect") + " ");
|
||||
|
||||
if (_savedServer.Type != "Socks5")
|
||||
// 可控Socks5
|
||||
text.Append($"Socks5 {i18N.Translate("Local Port", ": ")}{_socks5Port}");
|
||||
|
||||
if (_savedMode.Type == 3 || _savedMode.Type == 5)
|
||||
// 有HTTP
|
||||
text.Append($" | HTTP {i18N.Translate("Local Port", ": ")}{_httpPort}");
|
||||
|
||||
return $" ({text})";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NTT 控制器
|
||||
/// </summary>
|
||||
public NTTController pNTTController;
|
||||
public NTTController pNTTController = new NTTController();
|
||||
|
||||
private string _localAddress;
|
||||
private int _redirectorTCPPort;
|
||||
private int _httpPort;
|
||||
private int _socks5Port;
|
||||
|
||||
[DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]
|
||||
public static extern uint FlushDNSResolverCache();
|
||||
@@ -29,41 +73,83 @@ namespace Netch.Controllers
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public bool Start(Server server, Mode mode)
|
||||
public async Task<bool> Start(Server server, Mode mode)
|
||||
{
|
||||
pNTTController ??= new NTTController();
|
||||
FlushDNSResolverCache();
|
||||
Logging.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}");
|
||||
|
||||
var result = false;
|
||||
#region Record Settings
|
||||
|
||||
_httpPort = Global.Settings.HTTPLocalPort;
|
||||
_socks5Port = Global.Settings.Socks5LocalPort;
|
||||
_redirectorTCPPort = Global.Settings.RedirectorTCPPort;
|
||||
_localAddress = Global.Settings.LocalAddress;
|
||||
_savedServer = server;
|
||||
_savedMode = mode;
|
||||
|
||||
#endregion
|
||||
|
||||
FlushDNSResolverCache();
|
||||
_ = Task.Run(Firewall.AddNetchFwRules);
|
||||
|
||||
bool result;
|
||||
if (server.Type == "Socks5")
|
||||
{
|
||||
result = mode.Type != 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (server.Type)
|
||||
pEncryptedProxyController = server.Type switch
|
||||
{
|
||||
case "SS":
|
||||
pEncryptedProxyController = new SSController();
|
||||
"SS" => new SSController(),
|
||||
"SSR" => new SSRController(),
|
||||
"VMess" => new VMessController(),
|
||||
"Trojan" => new TrojanController(),
|
||||
_ => pEncryptedProxyController
|
||||
};
|
||||
|
||||
KillProcessByName(pEncryptedProxyController.MainFile);
|
||||
|
||||
// 检查端口是否被占用
|
||||
var isPortNotAvailable = false;
|
||||
if (_savedServer.Type != "Socks5")
|
||||
{
|
||||
isPortNotAvailable |= PortCheckAndShowMessageBox(_socks5Port, "Socks5");
|
||||
}
|
||||
|
||||
switch (_savedMode.Type)
|
||||
{
|
||||
case 0:
|
||||
isPortNotAvailable |= PortCheckAndShowMessageBox(_redirectorTCPPort, "Redirector TCP");
|
||||
break;
|
||||
case "SSR":
|
||||
pEncryptedProxyController = new SSRController();
|
||||
break;
|
||||
case "VMess":
|
||||
pEncryptedProxyController = new VMessController();
|
||||
break;
|
||||
case "Trojan":
|
||||
pEncryptedProxyController = new TrojanController();
|
||||
case 3:
|
||||
case 5:
|
||||
isPortNotAvailable |= PortCheckAndShowMessageBox(_httpPort, "HTTP");
|
||||
break;
|
||||
}
|
||||
|
||||
MainForm.Instance.StatusText(i18N.Translate("Starting ", pEncryptedProxyController.AkaName));
|
||||
if (pEncryptedProxyController.Ready) result = pEncryptedProxyController.Start(server, mode);
|
||||
if (isPortNotAvailable)
|
||||
{
|
||||
Logging.Error("主控制器启动失败: 端口被占用");
|
||||
return false;
|
||||
}
|
||||
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", pEncryptedProxyController.Name));
|
||||
try
|
||||
{
|
||||
result = await Task.Run(() => pEncryptedProxyController.Start(server, mode));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error("加密代理启动失败,未处理异常: " + e);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
// 加密代理已启动
|
||||
// 加密代理成功启动
|
||||
UsingPorts.Add(Global.Settings.Socks5LocalPort); // 记录Socks5使用端口
|
||||
|
||||
switch (mode.Type)
|
||||
{
|
||||
case 0: // 进程代理模式
|
||||
@@ -82,32 +168,69 @@ namespace Netch.Controllers
|
||||
break;
|
||||
}
|
||||
|
||||
if (pModeController != null && pModeController.Ready)
|
||||
if (pModeController != null)
|
||||
{
|
||||
MainForm.Instance.StatusText(i18N.Translate("Starting ", pModeController.AkaName));
|
||||
result = pModeController.Start(server, mode);
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", pModeController.Name));
|
||||
try
|
||||
{
|
||||
result = await Task.Run(() => pModeController.Start(server, mode));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is DllNotFoundException || e is FileNotFoundException)
|
||||
MessageBoxX.Show(e.Message + "\n\n" + i18N.Translate("Missing File or runtime components"), owner: Global.MainForm);
|
||||
else
|
||||
Logging.Error("模式启动失败,未处理异常: " + e);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (mode.Type)
|
||||
if (result)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
if (result)
|
||||
Task.Run(() =>
|
||||
// 成功启动
|
||||
|
||||
switch (mode.Type) // 记录使用端口
|
||||
{
|
||||
case 0:
|
||||
UsingPorts.Add(_redirectorTCPPort);
|
||||
break;
|
||||
case 3:
|
||||
case 5:
|
||||
UsingPorts.Add(_httpPort);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (mode.Type)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
MainForm.Instance.NatTypeStatusText(i18N.Translate("Starting NatTester"));
|
||||
Global.MainForm.NatTypeStatusText(i18N.Translate("Starting NatTester"));
|
||||
// Thread.Sleep(1000);
|
||||
var (nttResult, natType, localEnd, publicEnd) = pNTTController.Start();
|
||||
var country = Utils.Utils.GetCityCode(publicEnd);
|
||||
|
||||
if (nttResult) MainForm.Instance.NatTypeStatusText(natType, country);
|
||||
if (nttResult) Global.MainForm.NatTypeStatusText(natType, country);
|
||||
});
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) Stop();
|
||||
if (!result)
|
||||
{
|
||||
Logging.Error("主控制器启动失败");
|
||||
try
|
||||
{
|
||||
await Stop();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -115,10 +238,16 @@ namespace Netch.Controllers
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
public async Task Stop()
|
||||
{
|
||||
pEncryptedProxyController?.Stop();
|
||||
pModeController?.Stop();
|
||||
var tasks = new Task[]
|
||||
{
|
||||
Task.Run(() => pEncryptedProxyController?.Stop()),
|
||||
Task.Run(() => UsingPorts.Clear()),
|
||||
Task.Run(() => pModeController?.Stop()),
|
||||
Task.Run(() => pNTTController.Stop())
|
||||
};
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
public static void KillProcessByName(string name)
|
||||
@@ -131,12 +260,26 @@ namespace Netch.Controllers
|
||||
}
|
||||
catch (Win32Exception e)
|
||||
{
|
||||
Logging.Error($"结束进程 {name} 错误:\n" + e);
|
||||
Logging.Error($"结束进程 {name} 错误:" + e.Message);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="portName"></param>
|
||||
/// <param name="portType"></param>
|
||||
/// <returns>端口是否被占用</returns>
|
||||
private static bool PortCheckAndShowMessageBox(int port, string portName, PortType portType = PortType.Both)
|
||||
{
|
||||
if (!PortHelper.PortInUse(port, portType)) return false;
|
||||
MessageBoxX.Show(i18N.TranslateFormat("The {0} port is in use.", portName));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32;
|
||||
using Netch.Models;
|
||||
@@ -9,6 +10,8 @@ namespace Netch.Controllers
|
||||
{
|
||||
public class HTTPController : ModeController
|
||||
{
|
||||
public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
|
||||
|
||||
/// <summary>
|
||||
/// 实例
|
||||
/// </summary>
|
||||
@@ -19,8 +22,7 @@ namespace Netch.Controllers
|
||||
|
||||
public HTTPController()
|
||||
{
|
||||
AkaName = "HTTP";
|
||||
Ready = true;
|
||||
Name = "HTTP";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -31,9 +33,8 @@ namespace Netch.Controllers
|
||||
/// <returns>是否启动成功</returns>
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
RecordPrevious();
|
||||
|
||||
try
|
||||
{
|
||||
if (server.Type == "Socks5")
|
||||
@@ -47,7 +48,7 @@ namespace Netch.Controllers
|
||||
pPrivoxyController.Start(server, mode);
|
||||
}
|
||||
|
||||
if (mode.Type != 5) NativeMethods.SetGlobal($"127.0.0.1:{Global.Settings.HTTPLocalPort}", "<local>");
|
||||
if (mode.Type == 3) NativeMethods.SetGlobal($"127.0.0.1:{Global.Settings.HTTPLocalPort}", IEProxyExceptions);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -62,19 +63,31 @@ namespace Netch.Controllers
|
||||
|
||||
private void RecordPrevious()
|
||||
{
|
||||
var registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
|
||||
if (registry == null)
|
||||
try
|
||||
{
|
||||
var registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
|
||||
if (registry == null)
|
||||
throw new Exception();
|
||||
|
||||
prevPAC = registry.GetValue("AutoConfigURL")?.ToString() ?? "";
|
||||
prevHTTP = registry.GetValue("ProxyServer")?.ToString() ?? "";
|
||||
prevBypass = registry.GetValue("ProxyOverride")?.ToString() ?? "";
|
||||
prevEnabled = registry.GetValue("ProxyEnable")?.Equals(1) ?? false; // HTTP Proxy Enabled
|
||||
|
||||
if (prevHTTP == $"127.0.0.1:{Global.Settings.HTTPLocalPort}")
|
||||
{
|
||||
prevEnabled = false;
|
||||
prevHTTP = "";
|
||||
}
|
||||
|
||||
if (prevPAC != "")
|
||||
prevEnabled = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
prevEnabled = false;
|
||||
prevPAC = prevHTTP = prevBypass = "";
|
||||
return;
|
||||
}
|
||||
|
||||
prevPAC = registry.GetValue("AutoConfigURL")?.ToString() ?? "";
|
||||
if ((registry.GetValue("ProxyEnable")?.Equals(1) ?? false) || prevPAC != "") prevEnabled = true;
|
||||
|
||||
prevHTTP = registry.GetValue("ProxyServer")?.ToString() ?? "";
|
||||
prevBypass = registry.GetValue("ProxyOverride")?.ToString() ?? "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,21 +95,23 @@ namespace Netch.Controllers
|
||||
/// </summary>
|
||||
public override void Stop()
|
||||
{
|
||||
try
|
||||
var tasks = new[]
|
||||
{
|
||||
pPrivoxyController.Stop();
|
||||
|
||||
NativeMethods.SetGlobal(prevHTTP, prevBypass);
|
||||
if (prevPAC != "")
|
||||
NativeMethods.SetURL(prevPAC);
|
||||
if (!prevEnabled)
|
||||
NativeMethods.SetDIRECT();
|
||||
prevEnabled = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error("停止HTTP控制器错误:\n" + e);
|
||||
}
|
||||
Task.Factory.StartNew(pPrivoxyController.Stop),
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
if (prevEnabled)
|
||||
{
|
||||
if (prevHTTP != "")
|
||||
NativeMethods.SetGlobal(prevHTTP, prevBypass);
|
||||
if (prevPAC != "")
|
||||
NativeMethods.SetURL(prevPAC);
|
||||
}
|
||||
else
|
||||
NativeMethods.SetDIRECT();
|
||||
})
|
||||
};
|
||||
Task.WaitAll(tasks);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.ServiceProcess;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
using nfapinet;
|
||||
@@ -15,19 +15,13 @@ namespace Netch.Controllers
|
||||
{
|
||||
private static readonly ServiceController NFService = new ServiceController("netfilter2");
|
||||
|
||||
private static readonly string BinDriver = "";
|
||||
private static readonly string BinDriver = string.Empty;
|
||||
private static readonly string SystemDriver = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
|
||||
|
||||
public static string DriverVersion(string file)
|
||||
{
|
||||
return File.Exists(file) ? FileVersionInfo.GetVersionInfo(file).FileVersion : "";
|
||||
}
|
||||
private static string[] _sysDns = { };
|
||||
|
||||
static NFController()
|
||||
{
|
||||
// 生成系统版本
|
||||
var winNTver = $"{Environment.OSVersion.Version.Major.ToString()}.{Environment.OSVersion.Version.Minor.ToString()}";
|
||||
switch (winNTver)
|
||||
switch ($"{Environment.OSVersion.Version.Major}.{Environment.OSVersion.Version.Minor}")
|
||||
{
|
||||
case "10.0":
|
||||
BinDriver = "Win-10.sys";
|
||||
@@ -41,7 +35,7 @@ namespace Netch.Controllers
|
||||
BinDriver = "Win-7.sys";
|
||||
break;
|
||||
default:
|
||||
Logging.Error($"不支持的系统版本:{winNTver}");
|
||||
Logging.Error($"不支持的系统版本:{Environment.OSVersion.Version}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,22 +44,24 @@ namespace Netch.Controllers
|
||||
|
||||
public NFController()
|
||||
{
|
||||
MainFile = "Redirector";
|
||||
ExtFiles = new[] {Path.GetFileName(BinDriver)};
|
||||
InitCheck();
|
||||
|
||||
if (!File.Exists(SystemDriver))
|
||||
{
|
||||
InstallDriver();
|
||||
}
|
||||
Name = "Redirector";
|
||||
MainFile = "Redirector.exe";
|
||||
StartedKeywords.Add("Started");
|
||||
StoppedKeywords.AddRange(new[] {"Failed", "Unable"});
|
||||
}
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!CheckDriverReady())
|
||||
Logging.Info("内置驱动版本: " + DriverVersion(BinDriver));
|
||||
if (DriverVersion(SystemDriver) != DriverVersion(BinDriver))
|
||||
{
|
||||
if (File.Exists(SystemDriver))
|
||||
{
|
||||
Logging.Info("系统驱动版本: " + DriverVersion(SystemDriver));
|
||||
Logging.Info("更新驱动");
|
||||
UninstallDriver();
|
||||
}
|
||||
|
||||
if (!InstallDriver())
|
||||
return false;
|
||||
}
|
||||
@@ -75,10 +71,10 @@ namespace Netch.Controllers
|
||||
processList += proc + ",";
|
||||
processList += "NTT.exe";
|
||||
|
||||
Instance = GetProcess("bin\\Redirector.exe");
|
||||
var argument = new StringBuilder();
|
||||
if (server.Type != "Socks5")
|
||||
{
|
||||
Instance.StartInfo.Arguments += $"-r 127.0.0.1:{Global.Settings.Socks5LocalPort} -p \"{processList}\"";
|
||||
argument.Append($"-r 127.0.0.1:{Global.Settings.Socks5LocalPort} -p \"{processList}\"");
|
||||
}
|
||||
|
||||
else
|
||||
@@ -90,31 +86,26 @@ namespace Netch.Controllers
|
||||
return false;
|
||||
}
|
||||
|
||||
Instance.StartInfo.Arguments += $"-r {result}:{server.Port} -p \"{processList}\"";
|
||||
if (!string.IsNullOrWhiteSpace(server.Username) && !string.IsNullOrWhiteSpace(server.Password)) Instance.StartInfo.Arguments += $" -username \"{server.Username}\" -password \"{server.Password}\"";
|
||||
argument.Append($"-r {result}:{server.Port} -p \"{processList}\"");
|
||||
if (!string.IsNullOrWhiteSpace(server.Username) && !string.IsNullOrWhiteSpace(server.Password))
|
||||
argument.Append($" -username \"{server.Username}\" -password \"{server.Password}\"");
|
||||
}
|
||||
|
||||
Instance.StartInfo.Arguments += $" -t {Global.Settings.RedirectorTCPPort}";
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
argument.Append($" -t {Global.Settings.RedirectorTCPPort}");
|
||||
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
State = State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
|
||||
for (var j = 0; j < 40; j++)
|
||||
if (!StartInstanceAuto(argument.ToString())) continue;
|
||||
if (Global.Settings.ModifySystemDNS)
|
||||
{
|
||||
Thread.Sleep(250);
|
||||
|
||||
if (State == State.Started) return true;
|
||||
//备份并替换系统DNS
|
||||
_sysDns = DNS.getSystemDns();
|
||||
string[] dns = {"1.1.1.1", "8.8.8.8"};
|
||||
DNS.SetDNS(dns);
|
||||
}
|
||||
|
||||
Logging.Error("NF 进程启动超时");
|
||||
Stop();
|
||||
if (!RestartService()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -131,11 +122,11 @@ namespace Netch.Controllers
|
||||
// 防止其他程序占用 重置 NF 百万连接数限制
|
||||
NFService.Stop();
|
||||
NFService.WaitForStatus(ServiceControllerStatus.Stopped);
|
||||
MainForm.Instance.StatusText(i18N.Translate("Starting netfilter2 Service"));
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting netfilter2 Service"));
|
||||
NFService.Start();
|
||||
break;
|
||||
case ServiceControllerStatus.Stopped:
|
||||
MainForm.Instance.StatusText(i18N.Translate("Starting netfilter2 Service"));
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting netfilter2 Service"));
|
||||
NFService.Start();
|
||||
break;
|
||||
}
|
||||
@@ -157,17 +148,19 @@ namespace Netch.Controllers
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CheckDriverReady()
|
||||
public static string DriverVersion(string file)
|
||||
{
|
||||
// 检查驱动是否存在
|
||||
if (!File.Exists(SystemDriver)) return false;
|
||||
|
||||
// 检查驱动版本号
|
||||
return DriverVersion(SystemDriver) == DriverVersion(BinDriver);
|
||||
return File.Exists(file) ? FileVersionInfo.GetVersionInfo(file).FileVersion : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载 NF 驱动
|
||||
/// </summary>
|
||||
/// <returns>是否成功卸载</returns>
|
||||
public static bool UninstallDriver()
|
||||
{
|
||||
Global.MainForm.StatusText(i18N.Translate("Uninstalling NF Service"));
|
||||
Logging.Info("卸载 NF 驱动");
|
||||
try
|
||||
{
|
||||
if (NFService.Status == ServiceControllerStatus.Running)
|
||||
@@ -182,22 +175,19 @@ namespace Netch.Controllers
|
||||
}
|
||||
|
||||
if (!File.Exists(SystemDriver)) return true;
|
||||
try
|
||||
{
|
||||
NFAPI.nf_unRegisterDriver("netfilter2");
|
||||
NFAPI.nf_unRegisterDriver("netfilter2");
|
||||
File.Delete(SystemDriver);
|
||||
|
||||
File.Delete(SystemDriver);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安装 NF 驱动
|
||||
/// </summary>
|
||||
/// <returns>驱动是否安装成功</returns>
|
||||
public static bool InstallDriver()
|
||||
{
|
||||
Logging.Info("安装驱动中");
|
||||
Logging.Info("安装 NF 驱动");
|
||||
try
|
||||
{
|
||||
File.Copy(BinDriver, SystemDriver);
|
||||
@@ -208,12 +198,12 @@ namespace Netch.Controllers
|
||||
return false;
|
||||
}
|
||||
|
||||
MainForm.Instance.StatusText(i18N.Translate("Register driver"));
|
||||
Global.MainForm.StatusText(i18N.Translate("Register driver"));
|
||||
// 注册驱动文件
|
||||
var result = NFAPI.nf_registerDriver("netfilter2");
|
||||
if (result == NF_STATUS.NF_STATUS_SUCCESS)
|
||||
{
|
||||
Logging.Info($"驱动安装成功,当前驱动版本:{DriverVersion(DriverVersion(SystemDriver))}");
|
||||
Logging.Info($"驱动安装成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -224,50 +214,15 @@ namespace Netch.Controllers
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (Instance.HasExited)
|
||||
State = State.Stopped;
|
||||
else if (e.Data.Contains("Started"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("Failed") || e.Data.Contains("Unable")) State = State.Stopped;
|
||||
}
|
||||
else if (State == State.Started)
|
||||
{
|
||||
if (e.Data.StartsWith("[APP][Bandwidth]"))
|
||||
{
|
||||
var splited = e.Data.Replace("[APP][Bandwidth]", "").Trim().Split(',');
|
||||
if (splited.Length == 2)
|
||||
{
|
||||
var uploadSplited = splited[0].Split(':');
|
||||
var downloadSplited = splited[1].Split(':');
|
||||
|
||||
if (uploadSplited.Length == 2 && downloadSplited.Length == 2)
|
||||
if (long.TryParse(uploadSplited[1], out var upload) && long.TryParse(downloadSplited[1], out var download))
|
||||
Task.Run(() => OnBandwidthUpdated(upload, download));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (Global.Settings.ModifySystemDNS)
|
||||
//恢复系统DNS
|
||||
DNS.SetDNS(_sysDns);
|
||||
});
|
||||
StopInstance();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 流量变动事件
|
||||
/// </summary>
|
||||
public event BandwidthUpdateHandler OnBandwidthUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// 流量变动处理器
|
||||
/// </summary>
|
||||
/// <param name="upload">上传</param>
|
||||
/// <param name="download">下载</param>
|
||||
public delegate void BandwidthUpdateHandler(long upload, long download);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
@@ -35,14 +35,16 @@ namespace Netch.Controllers
|
||||
|
||||
public TUNTAPController()
|
||||
{
|
||||
MainFile = "tun2socks";
|
||||
InitCheck();
|
||||
Name = "tun2socks";
|
||||
MainFile = "tun2socks.exe";
|
||||
StartedKeywords.Add("Running");
|
||||
StoppedKeywords.AddRange(new[] {"failed", "invalid vconfig file"});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置 TUNTAP 适配器
|
||||
/// </summary>
|
||||
public bool Configure()
|
||||
private bool Configure()
|
||||
{
|
||||
// 查询服务器 IP 地址
|
||||
var destination = Dns.GetHostAddressesAsync(_savedServer.Hostname);
|
||||
@@ -54,154 +56,113 @@ namespace Netch.Controllers
|
||||
}
|
||||
|
||||
// 搜索出口
|
||||
return SearchOutbounds();
|
||||
return SearchAdapters();
|
||||
}
|
||||
|
||||
private readonly List<IPNetwork> _directIPs = new List<IPNetwork>();
|
||||
private readonly List<IPNetwork> _proxyIPs = new List<IPNetwork>();
|
||||
|
||||
/// <summary>
|
||||
/// 设置绕行规则
|
||||
/// </summary>
|
||||
public bool SetupBypass()
|
||||
/// <returns>是否设置成功</returns>
|
||||
private bool SetupRouteTable()
|
||||
{
|
||||
MainForm.Instance.StatusText(i18N.Translate("SetupBypass"));
|
||||
Logging.Info("设置绕行规则 → 设置让服务器 IP 走直连");
|
||||
// 让服务器 IP 走直连
|
||||
foreach (var address in _serverAddresses)
|
||||
if (!IPAddress.IsLoopback(address))
|
||||
NativeMethods.CreateRoute(address.ToString(), 32, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
Logging.Info("收集路由表规则");
|
||||
Global.MainForm.StatusText(i18N.Translate("SetupBypass"));
|
||||
|
||||
Logging.Info("绕行 → 全局绕过 IP");
|
||||
_directIPs.AddRange(Global.Settings.BypassIPs.Select(IPNetwork.Parse));
|
||||
|
||||
Logging.Info("绕行 → 服务器 IP");
|
||||
_directIPs.AddRange(_serverAddresses.Where(address => !IPAddress.IsLoopback(address)).Select(address => IPNetwork.Parse(address.ToString(), 32)));
|
||||
|
||||
// 处理模式的绕过中国
|
||||
if (_savedMode.BypassChina)
|
||||
{
|
||||
Logging.Info("设置绕行规则 → 处理模式的绕过中国");
|
||||
using (var sr = new StringReader(Encoding.UTF8.GetString(Resources.CNIP)))
|
||||
{
|
||||
string text;
|
||||
|
||||
while ((text = sr.ReadLine()) != null)
|
||||
{
|
||||
var info = text.Split('/');
|
||||
|
||||
NativeMethods.CreateRoute(info[0], int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
Logging.Info("绕行 → 中国 IP");
|
||||
_directIPs.AddRange(Encoding.UTF8.GetString(Resources.CNIP).Split('\n').Select(IPNetwork.Parse));
|
||||
}
|
||||
|
||||
Logging.Info("设置绕行规则 → 处理全局绕过 IP");
|
||||
// 处理全局绕过 IP
|
||||
foreach (var ip in Global.Settings.BypassIPs)
|
||||
Logging.Info("绕行 → 局域网 IP");
|
||||
_directIPs.AddRange(_bypassLanIPs.Select(IPNetwork.Parse));
|
||||
|
||||
switch (_savedMode.Type)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
var address = IPAddress.Parse(info[0]);
|
||||
case 1:
|
||||
// 代理规则
|
||||
Logging.Info("代理 → 规则 IP");
|
||||
_proxyIPs.AddRange(_savedMode.Rule.Select(IPNetwork.Parse));
|
||||
|
||||
if (!IPAddress.IsLoopback(address)) NativeMethods.CreateRoute(address.ToString(), int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
|
||||
Logging.Info("设置绕行规则 → 处理绕过局域网 IP");
|
||||
// 处理绕过局域网 IP
|
||||
foreach (var ip in _bypassLanIPs)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
var address = IPAddress.Parse(info[0]);
|
||||
|
||||
if (!IPAddress.IsLoopback(address)) NativeMethods.CreateRoute(address.ToString(), int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
|
||||
if (_savedMode.Type == 2) // 处理仅规则内走直连
|
||||
{
|
||||
Logging.Info("设置绕行规则 → 处理仅规则内走直连");
|
||||
// 将 TUN/TAP 网卡权重放到最高
|
||||
var instance = new Process
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
FileName = "netsh",
|
||||
Arguments = string.Format("interface ip set interface {0} metric=0", Global.TUNTAP.Index),
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = true,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
instance.Start();
|
||||
|
||||
Logging.Info("设置绕行规则 → 创建默认路由");
|
||||
// 创建默认路由
|
||||
if (!NativeMethods.CreateRoute("0.0.0.0", 0, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index, 10))
|
||||
{
|
||||
State = State.Stopped;
|
||||
|
||||
foreach (var address in _serverAddresses) NativeMethods.DeleteRoute(address.ToString(), 32, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Logging.Info("设置绕行规则 → 创建规则路由");
|
||||
// 创建规则路由
|
||||
foreach (var ip in _savedMode.Rule)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
|
||||
if (info.Length == 2)
|
||||
if (int.TryParse(info[1], out var prefix))
|
||||
NativeMethods.CreateRoute(info[0], prefix, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
else if (_savedMode.Type == 1) // 处理仅规则内走代理
|
||||
{
|
||||
Logging.Info("设置绕行规则->处理仅规则内走代理");
|
||||
foreach (var ip in _savedMode.Rule)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
|
||||
if (info.Length == 2)
|
||||
if (int.TryParse(info[1], out var prefix))
|
||||
NativeMethods.CreateRoute(info[0], prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
}
|
||||
|
||||
//处理 NAT 类型检测,由于协议的原因,无法仅通过域名确定需要代理的 IP,自己记录解析了返回的 IP,仅支持默认检测服务器
|
||||
if (Global.Settings.STUN_Server == "stun.stunprotocol.org")
|
||||
try
|
||||
{
|
||||
var nttAddress = Dns.GetHostAddresses(Global.Settings.STUN_Server)[0];
|
||||
if (int.TryParse("32", out var prefix)) NativeMethods.CreateRoute(nttAddress.ToString(), prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
|
||||
var nttrAddress = Dns.GetHostAddresses("stunresponse.coldthunder11.com")[0];
|
||||
if (int.TryParse("32", out var prefixr)) NativeMethods.CreateRoute(nttrAddress.ToString(), prefixr, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logging.Info("NAT 类型测试域名解析失败,将不会被添加到代理列表");
|
||||
}
|
||||
|
||||
//处理DNS代理
|
||||
if (Global.Settings.TUNTAP.ProxyDNS)
|
||||
{
|
||||
Logging.Info("设置绕行规则 → 处理自定义 DNS 代理");
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
var dns = "";
|
||||
foreach (var value in Global.Settings.TUNTAP.DNS)
|
||||
//处理 NAT 类型检测,由于协议的原因,无法仅通过域名确定需要代理的 IP,自己记录解析了返回的 IP,仅支持默认检测服务器
|
||||
if (Global.Settings.STUN_Server == "stun.stunprotocol.org")
|
||||
try
|
||||
{
|
||||
dns += value;
|
||||
dns += ',';
|
||||
Logging.Info("代理 → STUN 服务器 IP");
|
||||
_proxyIPs.AddRange(new[]
|
||||
{
|
||||
Dns.GetHostAddresses(Global.Settings.STUN_Server)[0],
|
||||
Dns.GetHostAddresses("stunresponse.coldthunder11.com")[0]
|
||||
}.Select(ip => IPNetwork.Parse(ip.ToString(), 32)));
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logging.Info("NAT 类型测试域名解析失败,将不会被添加到代理列表");
|
||||
}
|
||||
|
||||
dns = dns.Trim();
|
||||
dns = dns.Substring(0, dns.Length - 1);
|
||||
if (int.TryParse("32", out var prefix)) NativeMethods.CreateRoute(dns, prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
}
|
||||
else
|
||||
if (Global.Settings.TUNTAP.ProxyDNS)
|
||||
{
|
||||
if (int.TryParse("32", out var prefix))
|
||||
Logging.Info("代理 → 自定义 DNS");
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
NativeMethods.CreateRoute("1.1.1.1", prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
NativeMethods.CreateRoute("8.8.8.8", prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
NativeMethods.CreateRoute("9.9.9.9", prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
NativeMethods.CreateRoute("185.222.222.222", prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
var dns = string.Empty;
|
||||
foreach (var value in Global.Settings.TUNTAP.DNS)
|
||||
{
|
||||
dns += value;
|
||||
dns += ',';
|
||||
}
|
||||
|
||||
dns = dns.Trim();
|
||||
dns = dns.Substring(0, dns.Length - 1);
|
||||
RouteAction(Action.Create, dns, 32, RouteType.TUNTAP);
|
||||
}
|
||||
else
|
||||
{
|
||||
_proxyIPs.AddRange(new[] {"1.1.1.1", "8.8.8.8", "9.9.9.9", "185.222.222.222"}.Select(ip => IPNetwork.Parse(ip, 32)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 2:
|
||||
// 绕过规则
|
||||
|
||||
// 将 TUN/TAP 网卡权重放到最高
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "netsh",
|
||||
Arguments = $"interface ip set interface {Global.TUNTAP.Index} metric=0",
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = true,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
);
|
||||
|
||||
Logging.Info("绕行 → 规则 IP");
|
||||
_directIPs.AddRange(_savedMode.Rule.Select(IPNetwork.Parse));
|
||||
|
||||
Logging.Info("代理 → 全局");
|
||||
if (!RouteAction(Action.Create, IPNetwork.Parse("0.0.0.0", 0), RouteType.TUNTAP))
|
||||
{
|
||||
State = State.Stopped;
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Logging.Info("设置路由规则");
|
||||
RouteAction(Action.Create, _directIPs, RouteType.Gateway);
|
||||
RouteAction(Action.Create, _proxyIPs, RouteType.TUNTAP);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -209,129 +170,41 @@ namespace Netch.Controllers
|
||||
/// <summary>
|
||||
/// 清除绕行规则
|
||||
/// </summary>
|
||||
public bool ClearBypass()
|
||||
private bool ClearBypass()
|
||||
{
|
||||
if (_savedMode.Type == 2)
|
||||
switch (_savedMode.Type)
|
||||
{
|
||||
NativeMethods.DeleteRoute("0.0.0.0", 0, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index, 10);
|
||||
|
||||
foreach (var ip in _savedMode.Rule)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
|
||||
if (info.Length == 2)
|
||||
if (int.TryParse(info[1], out var prefix))
|
||||
NativeMethods.DeleteRoute(info[0], prefix, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
else if (_savedMode.Type == 1)
|
||||
{
|
||||
foreach (var ip in _savedMode.Rule)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
|
||||
if (info.Length == 2)
|
||||
if (int.TryParse(info[1], out var prefix))
|
||||
NativeMethods.DeleteRoute(info[0], prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
}
|
||||
|
||||
if (Global.Settings.STUN_Server == "stun.stunprotocol.org")
|
||||
try
|
||||
{
|
||||
var nttAddress = Dns.GetHostAddresses(Global.Settings.STUN_Server)[0];
|
||||
if (int.TryParse("32", out var prefix)) NativeMethods.DeleteRoute(nttAddress.ToString(), prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
|
||||
var nttrAddress = Dns.GetHostAddresses("stunresponse.coldthunder11.com")[0];
|
||||
if (int.TryParse("32", out var prefixr)) NativeMethods.DeleteRoute(nttrAddress.ToString(), prefixr, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (Global.Settings.TUNTAP.ProxyDNS)
|
||||
{
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
var dns = "";
|
||||
foreach (var value in Global.Settings.TUNTAP.DNS)
|
||||
{
|
||||
dns += value;
|
||||
dns += ',';
|
||||
}
|
||||
|
||||
dns = dns.Trim();
|
||||
dns = dns.Substring(0, dns.Length - 1);
|
||||
if (int.TryParse("32", out var prefix)) NativeMethods.DeleteRoute(dns, prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (int.TryParse("32", out var prefix)) NativeMethods.DeleteRoute("1.1.1.1", prefix, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index);
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
RouteAction(Action.Delete, "0.0.0.0", 0, RouteType.TUNTAP, 10);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var ip in Global.Settings.BypassIPs)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
var address = IPAddress.Parse(info[0]);
|
||||
|
||||
if (!IPAddress.IsLoopback(address)) NativeMethods.DeleteRoute(address.ToString(), int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
|
||||
foreach (var ip in _bypassLanIPs)
|
||||
{
|
||||
var info = ip.Split('/');
|
||||
var address = IPAddress.Parse(info[0]);
|
||||
|
||||
if (!IPAddress.IsLoopback(address)) NativeMethods.DeleteRoute(address.ToString(), int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
|
||||
if (_savedMode.BypassChina)
|
||||
using (var sr = new StringReader(Encoding.UTF8.GetString(Resources.CNIP)))
|
||||
{
|
||||
string text;
|
||||
|
||||
while ((text = sr.ReadLine()) != null)
|
||||
{
|
||||
var info = text.Split('/');
|
||||
|
||||
NativeMethods.DeleteRoute(info[0], int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var address in _serverAddresses)
|
||||
if (!IPAddress.IsLoopback(address))
|
||||
NativeMethods.DeleteRoute(address.ToString(), 32, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
|
||||
|
||||
RouteAction(Action.Delete, _directIPs, RouteType.Gateway);
|
||||
RouteAction(Action.Delete, _proxyIPs, RouteType.TUNTAP);
|
||||
_directIPs.Clear();
|
||||
_proxyIPs.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
MainForm.Instance.StatusText(i18N.Translate("Starting Tap"));
|
||||
|
||||
_savedMode = mode;
|
||||
_savedServer = server;
|
||||
|
||||
if (!Configure()) return false;
|
||||
|
||||
Logging.Info("设置绕行规则");
|
||||
SetupBypass();
|
||||
Logging.Info("设置绕行规则完毕");
|
||||
|
||||
Instance = GetProcess("bin\\tun2socks.exe");
|
||||
SetupRouteTable();
|
||||
|
||||
var adapterName = TUNTAP.GetName(Global.TUNTAP.ComponentID);
|
||||
Logging.Info($"tun2sock使用适配器:{adapterName}");
|
||||
|
||||
string dns;
|
||||
//V2ray使用Unbound本地DNS会导致查询异常缓慢故此V2ray不启动unbound而是使用自定义DNS
|
||||
//if (Global.Settings.TUNTAP.UseCustomDNS || server.Type.Equals("VMess"))
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
dns = "";
|
||||
dns = string.Empty;
|
||||
foreach (var value in Global.Settings.TUNTAP.DNS)
|
||||
{
|
||||
dns += value;
|
||||
@@ -349,34 +222,17 @@ namespace Netch.Controllers
|
||||
|
||||
if (Global.Settings.TUNTAP.UseFakeDNS) dns += " -fakeDns";
|
||||
|
||||
var argument = new StringBuilder();
|
||||
if (server.Type == "Socks5")
|
||||
Instance.StartInfo.Arguments = $"-proxyServer {server.Hostname}:{server.Port} -tunAddr {Global.Settings.TUNTAP.Address} -tunMask {Global.Settings.TUNTAP.Netmask} -tunGw {Global.Settings.TUNTAP.Gateway} -tunDns {dns} -tunName \"{adapterName}\"";
|
||||
argument.Append($"-proxyServer {server.Hostname}:{server.Port} ");
|
||||
else
|
||||
Instance.StartInfo.Arguments = $"-proxyServer 127.0.0.1:{Global.Settings.Socks5LocalPort} -tunAddr {Global.Settings.TUNTAP.Address} -tunMask {Global.Settings.TUNTAP.Netmask} -tunGw {Global.Settings.TUNTAP.Gateway} -tunDns {dns} -tunName \"{adapterName}\"";
|
||||
argument.Append($"-proxyServer 127.0.0.1:{Global.Settings.Socks5LocalPort} ");
|
||||
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
argument.Append($"-tunAddr {Global.Settings.TUNTAP.Address} -tunMask {Global.Settings.TUNTAP.Netmask} -tunGw {Global.Settings.TUNTAP.Gateway} -tunDns {dns} -tunName \"{adapterName}\"");
|
||||
|
||||
State = State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginErrorReadLine();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.PriorityClass = ProcessPriorityClass.RealTime;
|
||||
|
||||
for (var i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
|
||||
if (State == State.Started) return true;
|
||||
|
||||
if (State == State.Stopped)
|
||||
{
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
if (!StartInstanceAuto(argument.ToString(), ProcessPriorityClass.RealTime)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -384,128 +240,153 @@ namespace Netch.Controllers
|
||||
/// </summary>
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
ClearBypass();
|
||||
pDNSController.Stop();
|
||||
}
|
||||
|
||||
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!WriteLog(e)) return;
|
||||
if (State == State.Starting)
|
||||
var tasks = new[]
|
||||
{
|
||||
if (e.Data.Contains("Running"))
|
||||
State = State.Started;
|
||||
else if (e.Data.Contains("failed") || e.Data.Contains("invalid vconfig file")) State = State.Stopped;
|
||||
}
|
||||
Task.Factory.StartNew(StopInstance),
|
||||
Task.Factory.StartNew(ClearBypass),
|
||||
Task.Factory.StartNew(pDNSController.Stop)
|
||||
};
|
||||
Task.WaitAll(tasks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 搜索出口
|
||||
/// 搜索出口和TUNTAP适配器
|
||||
/// </summary>
|
||||
public static bool SearchOutbounds()
|
||||
private static bool SearchAdapters()
|
||||
{
|
||||
Logging.Info("正在搜索出口中");
|
||||
|
||||
if (Win32Native.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse("114.114.114.114").GetAddressBytes(), 0), 0, out var pRoute) == 0)
|
||||
// 寻找出口适配器
|
||||
if (Win32Native.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse("114.114.114.114").GetAddressBytes(), 0), 0, out var pRoute) != 0)
|
||||
{
|
||||
Global.Adapter.Index = pRoute.dwForwardIfIndex;
|
||||
Global.Adapter.Gateway = new IPAddress(pRoute.dwForwardNextHop);
|
||||
Logging.Info($"当前 网关 地址:{Global.Adapter.Gateway}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Error("GetBestRoute 搜索失败");
|
||||
Logging.Error("GetBestRoute 搜索失败(找不到出口适配器)");
|
||||
return false;
|
||||
}
|
||||
|
||||
Logging.Info($"搜索适配器index:{Global.Adapter.Index}");
|
||||
var AddressGot = false;
|
||||
foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces())
|
||||
try
|
||||
{
|
||||
var adapterProperties = adapter.GetIPProperties();
|
||||
var p = adapterProperties.GetIPv4Properties();
|
||||
Logging.Info($"检测适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {p.Index}");
|
||||
|
||||
// 通过索引查找对应适配器的 IPv4 地址
|
||||
if (p.Index == Global.Adapter.Index)
|
||||
{
|
||||
var AdapterIPs = "";
|
||||
|
||||
foreach (var ip in adapterProperties.UnicastAddresses)
|
||||
{
|
||||
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
AddressGot = true;
|
||||
Global.Adapter.Address = ip.Address;
|
||||
Logging.Info($"当前出口 IPv4 地址:{Global.Adapter.Address}");
|
||||
break;
|
||||
}
|
||||
|
||||
AdapterIPs = $"{ip.Address} | ";
|
||||
}
|
||||
|
||||
if (!AddressGot)
|
||||
{
|
||||
if (AdapterIPs.Length > 3)
|
||||
{
|
||||
AdapterIPs = AdapterIPs.Substring(0, AdapterIPs.Length - 3);
|
||||
Logging.Info($"所有出口地址:{AdapterIPs}");
|
||||
}
|
||||
|
||||
Logging.Error("出口无 IPv4 地址,当前只支持 IPv4 地址");
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (!AddressGot)
|
||||
{
|
||||
Logging.Error("无法找到当前使用适配器");
|
||||
return false;
|
||||
}
|
||||
Global.Adapter.Index = pRoute.dwForwardIfIndex;
|
||||
|
||||
// 搜索 TUN/TAP 适配器的索引
|
||||
Global.TUNTAP.ComponentID = TUNTAP.GetComponentID();
|
||||
if (string.IsNullOrEmpty(Global.TUNTAP.ComponentID))
|
||||
if (string.IsNullOrEmpty(Global.TUNTAP.ComponentID = TUNTAP.GetComponentID()))
|
||||
{
|
||||
Logging.Error("未找到可用 TUN/TAP 适配器");
|
||||
Logging.Info("找不到 TAP 适配器");
|
||||
if (MessageBoxX.Show(i18N.Translate("TUN/TAP driver is not detected. Is it installed now?"), confirm: true) == DialogResult.OK)
|
||||
{
|
||||
Configuration.addtap();
|
||||
//给点时间,不然立马安装完毕就查找适配器可能会导致找不到适配器ID
|
||||
// 给点时间,不然立马安装完毕就查找适配器可能会导致找不到适配器ID
|
||||
Thread.Sleep(1000);
|
||||
Global.TUNTAP.ComponentID = TUNTAP.GetComponentID();
|
||||
if (string.IsNullOrEmpty(Global.TUNTAP.ComponentID = TUNTAP.GetComponentID()))
|
||||
{
|
||||
Logging.Error("找不到 TAP 适配器,驱动可能安装失败");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Info("取消安装 TAP 驱动 ");
|
||||
return false;
|
||||
}
|
||||
|
||||
//MessageBoxX.Show(i18N.Translate("Please install TAP-Windows and create an TUN/TAP adapter manually"));
|
||||
// return false;
|
||||
}
|
||||
|
||||
foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces())
|
||||
if (adapter.Id == Global.TUNTAP.ComponentID)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
Global.TUNTAP.Adapter = adapter;
|
||||
Global.TUNTAP.Index = adapter.GetIPProperties().GetIPv4Properties().Index;
|
||||
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ => _.GetIPProperties().GetIPv4Properties().Index == Global.Adapter.Index);
|
||||
Global.Adapter.Address = adapter.GetIPProperties().UnicastAddresses.First(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork).Address;
|
||||
Global.Adapter.Gateway = new IPAddress(pRoute.dwForwardNextHop);
|
||||
Logging.Info($"出口 IPv4 地址:{Global.Adapter.Address}");
|
||||
Logging.Info($"出口 网关 地址:{Global.Adapter.Gateway}");
|
||||
Logging.Info($"出口适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.Adapter.Index}");
|
||||
// Ex NetworkInformationException: 此接口不支持 IPv4 协议。
|
||||
|
||||
Logging.Info($"找到适配器TUN/TAP:{adapter.Id}");
|
||||
|
||||
return true;
|
||||
// Ex NetworkInformationException: Windows 系统函数调用失败。
|
||||
// Ex System.ArgumentNullException: source 或 predicate 为 null。
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InvalidOperationException)
|
||||
Logging.Error($"找不到网络接口索引为 {Global.Adapter.Index} 的出口适配器");
|
||||
throw;
|
||||
}
|
||||
|
||||
Logging.Error("无法找到出口");
|
||||
return false;
|
||||
try
|
||||
{
|
||||
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ => _.Id == Global.TUNTAP.ComponentID);
|
||||
Global.TUNTAP.Adapter = adapter;
|
||||
Global.TUNTAP.Index = adapter.GetIPProperties().GetIPv4Properties().Index;
|
||||
Logging.Info($"TAP 适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.TUNTAP.Index}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InvalidOperationException)
|
||||
Logging.Error($"找不到标识符为 {Global.TUNTAP.ComponentID} 的 TAP 适配器");
|
||||
throw;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// 理论上如果得到了网络接口的索引/网络适配器的标识符不会找不到网卡
|
||||
// 只是异常处理
|
||||
Logging.Info("所有适配器:\n" +
|
||||
NetworkInterface.GetAllNetworkInterfaces().Aggregate(string.Empty, (current, adapter)
|
||||
=> current + $"{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.TUNTAP.Index}{Global.EOF}"));
|
||||
return false;
|
||||
}
|
||||
catch (NetworkInformationException e)
|
||||
{
|
||||
if (e.ErrorCode == 10043)
|
||||
MessageBoxX.Show("适配器未开启IPv4协议", LogLevel.ERROR, owner: Global.MainForm);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private enum RouteType
|
||||
{
|
||||
Gateway,
|
||||
TUNTAP
|
||||
}
|
||||
|
||||
private enum Action
|
||||
{
|
||||
Create,
|
||||
Delete
|
||||
}
|
||||
|
||||
private static bool RouteAction(Action action, IEnumerable<IPNetwork> ipNetworks, RouteType routeType, int metric = 0)
|
||||
{
|
||||
return ipNetworks.All(address => RouteAction(action, address, routeType, metric));
|
||||
}
|
||||
|
||||
private static bool RouteAction(Action action, string address, byte cidr, RouteType routeType, int metric = 0)
|
||||
{
|
||||
return RouteAction(action, IPNetwork.Parse(address, cidr), routeType, metric);
|
||||
}
|
||||
|
||||
private static bool RouteAction(Action action, IPNetwork ipNetwork, RouteType routeType, int metric = 0)
|
||||
{
|
||||
string gateway;
|
||||
int index;
|
||||
switch (routeType)
|
||||
{
|
||||
case RouteType.Gateway:
|
||||
gateway = Global.Adapter.Gateway.ToString();
|
||||
index = Global.Adapter.Index;
|
||||
break;
|
||||
case RouteType.TUNTAP:
|
||||
gateway = Global.Settings.TUNTAP.Gateway;
|
||||
index = Global.TUNTAP.Index;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(routeType), routeType, null);
|
||||
}
|
||||
|
||||
return action switch
|
||||
{
|
||||
Action.Create => NativeMethods.CreateRoute(ipNetwork.Network.ToString(), ipNetwork.Cidr, gateway, index, metric),
|
||||
Action.Delete => NativeMethods.DeleteRoute(ipNetwork.Network.ToString(), ipNetwork.Cidr, gateway, index, metric),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(action), action, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Controllers
|
||||
@@ -11,8 +11,8 @@ namespace Netch.Controllers
|
||||
|
||||
public NTTController()
|
||||
{
|
||||
MainFile = "NTT";
|
||||
InitCheck();
|
||||
Name = "NTT";
|
||||
MainFile = "NTT.exe";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -21,17 +21,11 @@ namespace Netch.Controllers
|
||||
/// <returns></returns>
|
||||
public (bool, string, string, string) Start()
|
||||
{
|
||||
if (!Ready) return (false, null, null, null);
|
||||
try
|
||||
{
|
||||
Instance = GetProcess("bin\\NTT.exe");
|
||||
|
||||
Instance.StartInfo.Arguments = $" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}";
|
||||
|
||||
InitInstance($" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}");
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
|
||||
State = State.Starting;
|
||||
Instance.Start();
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
@@ -44,25 +38,31 @@ namespace Netch.Controllers
|
||||
|
||||
return (true, natType, localEnd, publicEnd);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error("NTT 进程出错");
|
||||
Stop();
|
||||
Logging.Error($"{Name} 控制器出错:\n" + e);
|
||||
try
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return (false, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
private new void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Data))
|
||||
_lastResult = e.Data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 无用
|
||||
/// </summary>
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Controllers
|
||||
@@ -9,15 +7,13 @@ namespace Netch.Controllers
|
||||
{
|
||||
public PrivoxyController()
|
||||
{
|
||||
MainFile = "Privoxy";
|
||||
ExtFiles = new[] {"default.conf"};
|
||||
InitCheck();
|
||||
Name = "Privoxy";
|
||||
MainFile = "Privoxy.exe";
|
||||
RedirectStd = false;
|
||||
}
|
||||
|
||||
public bool Start(Server server, Mode mode)
|
||||
{
|
||||
if (!Ready) return false;
|
||||
|
||||
var isSocks5 = server.Type == "Socks5";
|
||||
var socks5Port = isSocks5 ? server.Port : Global.Settings.Socks5LocalPort;
|
||||
var text = File.ReadAllText("bin\\default.conf")
|
||||
@@ -28,20 +24,7 @@ namespace Netch.Controllers
|
||||
text = text.Replace("/ 127.0.0.1", $"/ {server.Hostname}");
|
||||
File.WriteAllText("data\\privoxy.conf", text);
|
||||
|
||||
Instance = GetProcess("bin\\Privoxy.exe", false);
|
||||
Instance.StartInfo.Arguments = "..\\data\\privoxy.conf";
|
||||
Instance.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
Instance.StartInfo.UseShellExecute = true;
|
||||
try
|
||||
{
|
||||
Instance.Start();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return StartInstanceAuto("..\\data\\privoxy.conf");
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Models.GitHubRelease;
|
||||
using Netch.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class UpdateChecker
|
||||
{
|
||||
private const string DefaultUserAgent = @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36";
|
||||
|
||||
private const int DefaultGetTimeout = 30000;
|
||||
|
||||
public const string Owner = @"NetchX";
|
||||
public const string Repo = @"Netch";
|
||||
|
||||
@@ -28,14 +24,14 @@ namespace Netch.Controllers
|
||||
public event EventHandler NewVersionFoundFailed;
|
||||
public event EventHandler NewVersionNotFound;
|
||||
|
||||
public async void Check(bool notifyNoFound, bool isPreRelease)
|
||||
public void Check(bool notifyNoFound, bool isPreRelease)
|
||||
{
|
||||
try
|
||||
{
|
||||
var updater = new GitHubRelease(Owner, Repo);
|
||||
var url = updater.AllReleaseUrl;
|
||||
|
||||
var json = await GetAsync(url, true);
|
||||
var json = WebUtil.DownloadString(WebUtil.CreateRequest(url));
|
||||
|
||||
var releases = JsonConvert.DeserializeObject<List<Release>>(json);
|
||||
var latestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease);
|
||||
@@ -53,29 +49,9 @@ namespace Netch.Controllers
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e.Message);
|
||||
Logging.Error(e.ToString());
|
||||
if (notifyNoFound) NewVersionFoundFailed?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string> GetAsync(string url, bool useProxy, string userAgent = @"", double timeout = DefaultGetTimeout)
|
||||
{
|
||||
var httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
UseProxy = useProxy
|
||||
};
|
||||
var httpClient = new HttpClient(httpClientHandler)
|
||||
{
|
||||
Timeout = TimeSpan.FromMilliseconds(timeout)
|
||||
};
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.Add(@"User-Agent", string.IsNullOrWhiteSpace(userAgent) ? DefaultUserAgent : userAgent);
|
||||
|
||||
var response = await httpClient.SendAsync(request);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
var resultStr = await response.Content.ReadAsStringAsync();
|
||||
return resultStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -15,9 +14,10 @@ namespace Netch.Forms
|
||||
|
||||
partial class MainForm
|
||||
{
|
||||
public void ControlFun()
|
||||
private bool _isFirstCloseWindow = true;
|
||||
|
||||
private async void ControlFun()
|
||||
{
|
||||
SaveConfigs();
|
||||
if (State == State.Waiting || State == State.Stopped)
|
||||
{
|
||||
// 服务器、模式 需选择
|
||||
@@ -29,150 +29,77 @@ namespace Netch.Forms
|
||||
|
||||
if (ModeComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select an mode first"));
|
||||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||||
return;
|
||||
}
|
||||
|
||||
//MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ControlButton.Enabled = SettingsButton.Enabled = false;
|
||||
// 清除模式搜索框文本选择
|
||||
ModeComboBox.Select(0, 0);
|
||||
|
||||
UpdateStatus(State.Starting);
|
||||
State = State.Starting;
|
||||
|
||||
Firewall.AddNetchFwRules();
|
||||
var server = ServerComboBox.SelectedItem as Models.Server;
|
||||
var mode = ModeComboBox.SelectedItem as Models.Mode;
|
||||
|
||||
Task.Run(() =>
|
||||
if (await _mainController.Start(server, mode))
|
||||
{
|
||||
var server = ServerComboBox.SelectedItem as Models.Server;
|
||||
var mode = ModeComboBox.SelectedItem as Models.Mode;
|
||||
|
||||
MainController ??= new MainController();
|
||||
|
||||
var startResult = MainController.Start(server, mode);
|
||||
|
||||
if (startResult)
|
||||
State = State.Started;
|
||||
_ = Task.Run(() => { Bandwidth.NetTraffic(server, mode, ref _mainController); });
|
||||
// 如果勾选启动后最小化
|
||||
if (Global.Settings.MinimizeWhenStarted)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
UpdateStatus(State.Started);
|
||||
StatusText(i18N.Translate(StateExtension.GetStatusString(State)) + PortText(server.Type,mode.Type));
|
||||
|
||||
LastUploadBandwidth = 0;
|
||||
//LastDownloadBandwidth = 0;
|
||||
//UploadSpeedLabel.Text = "↑: 0 KB/s";
|
||||
DownloadSpeedLabel.Text = "↑↓: 0 KB/s";
|
||||
UsedBandwidthLabel.Text = $"{i18N.Translate("Used",": ")}0 KB";
|
||||
UsedBandwidthLabel.Visible = UploadSpeedLabel.Visible = DownloadSpeedLabel.Visible = true;
|
||||
UploadSpeedLabel.Visible = false;
|
||||
Bandwidth.NetTraffic(server, mode, MainController);
|
||||
});
|
||||
WindowState = FormWindowState.Minimized;
|
||||
|
||||
// 如果勾选启动后最小化
|
||||
if (Global.Settings.MinimizeWhenStarted)
|
||||
if (_isFirstCloseWindow)
|
||||
{
|
||||
WindowState = FormWindowState.Minimized;
|
||||
NotifyIcon.Visible = true;
|
||||
// 显示提示语
|
||||
NotifyTip(i18N.Translate("Netch is now minimized to the notification bar, double click this icon to restore."));
|
||||
_isFirstCloseWindow = false;
|
||||
}
|
||||
|
||||
if (IsFirstOpened)
|
||||
Hide();
|
||||
}
|
||||
|
||||
if (Global.Settings.StartedTcping)
|
||||
{
|
||||
// 自动检测延迟
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
while (State == State.Started)
|
||||
{
|
||||
// 显示提示语
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
UpdateChecker.Name,
|
||||
i18N.Translate(
|
||||
"Netch is now minimized to the notification bar, double click this icon to restore."),
|
||||
ToolTipIcon.Info);
|
||||
server.Test();
|
||||
// 重载服务器列表
|
||||
InitServer();
|
||||
|
||||
IsFirstOpened = false;
|
||||
Thread.Sleep(Global.Settings.StartedTcping_Interval * 1000);
|
||||
}
|
||||
|
||||
Hide();
|
||||
}
|
||||
|
||||
if (Global.Settings.StartedTcping)
|
||||
{
|
||||
// 自动检测延迟
|
||||
Task.Run(() =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (State == State.Started)
|
||||
{
|
||||
server.Test();
|
||||
// 重载服务器列表
|
||||
InitServer();
|
||||
|
||||
Thread.Sleep(Global.Settings.StartedTcping_Interval * 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateStatus(State.Stopped);
|
||||
StatusText(i18N.Translate("Start failed"));
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
State = State.Stopped;
|
||||
StatusText(i18N.Translate("Start failed"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 停止
|
||||
UpdateStatus(State.Stopping);
|
||||
MainController.Stop();
|
||||
UpdateStatus(State.Stopped);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
TestServer();
|
||||
});
|
||||
State = State.Stopping;
|
||||
await _mainController.Stop();
|
||||
State = State.Stopped;
|
||||
_ = Task.Run(TestServer);
|
||||
}
|
||||
}
|
||||
|
||||
private string PortText(string serverType,int modeType)
|
||||
{
|
||||
var text = new StringBuilder(" (");
|
||||
text.Append(Global.Settings.LocalAddress == "0.0.0.0"
|
||||
? i18N.Translate("Allow other Devices to connect") + " "
|
||||
: "");
|
||||
if (serverType == "Socks5")
|
||||
{
|
||||
// 不可控Socks5
|
||||
if (modeType == 3 || modeType == 5)
|
||||
{
|
||||
// 可控HTTP
|
||||
text.Append(
|
||||
$"HTTP {i18N.Translate("Local Port", ": ")}{Global.Settings.HTTPLocalPort}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 不可控HTTP
|
||||
text.Clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 可控Socks5
|
||||
text.Append(
|
||||
$"Socks5 {i18N.Translate("Local Port", ": ")}{Global.Settings.Socks5LocalPort}");
|
||||
if (modeType == 3 || modeType == 5)
|
||||
{
|
||||
//有HTTP
|
||||
text.Append(
|
||||
$" | HTTP {i18N.Translate("Local Port", ": ")}{Global.Settings.HTTPLocalPort}");
|
||||
}
|
||||
}
|
||||
if (text.Length > 0)
|
||||
{
|
||||
text.Append(")");
|
||||
}
|
||||
|
||||
return text.ToString();
|
||||
}
|
||||
|
||||
public void OnBandwidthUpdated(long download)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action<long>(OnBandwidthUpdated), download);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
UsedBandwidthLabel.Text = $"{i18N.Translate("Used", ": ")}{Bandwidth.Compute(download)}";
|
||||
@@ -183,13 +110,20 @@ namespace Netch.Forms
|
||||
LastDownloadBandwidth = download;
|
||||
Refresh();
|
||||
}
|
||||
catch (Exception)
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBandwidthUpdated(long upload, long download)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action<long, long>(OnBandwidthUpdated), upload, download);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (upload < 1 || download < 1)
|
||||
@@ -198,7 +132,7 @@ namespace Netch.Forms
|
||||
}
|
||||
|
||||
UsedBandwidthLabel.Text =
|
||||
$"{i18N.Translate("Used",": ")}{Bandwidth.Compute(upload + download)}";
|
||||
$"{i18N.Translate("Used", ": ")}{Bandwidth.Compute(upload + download)}";
|
||||
UploadSpeedLabel.Text = $"↑: {Bandwidth.Compute(upload - LastUploadBandwidth)}/s";
|
||||
DownloadSpeedLabel.Text = $"↓: {Bandwidth.Compute(download - LastDownloadBandwidth)}/s";
|
||||
|
||||
@@ -206,8 +140,9 @@ namespace Netch.Forms
|
||||
LastDownloadBandwidth = download;
|
||||
Refresh();
|
||||
}
|
||||
catch (Exception)
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
Netch/Forms/MainForm.Designer.cs
generated
18
Netch/Forms/MainForm.Designer.cs
generated
@@ -56,7 +56,6 @@ namespace Netch.Forms
|
||||
this.AboutToolStripButton = new System.Windows.Forms.ToolStripButton();
|
||||
this.VersionLabel = new System.Windows.Forms.ToolStripLabel();
|
||||
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.RelyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.configLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.ProfileLabel = new System.Windows.Forms.Label();
|
||||
@@ -116,8 +115,7 @@ namespace Netch.Forms
|
||||
this.OptionsToolStripMenuItem,
|
||||
this.AboutToolStripButton,
|
||||
this.VersionLabel,
|
||||
this.exitToolStripMenuItem,
|
||||
this.RelyToolStripMenuItem});
|
||||
this.exitToolStripMenuItem});
|
||||
this.MenuStrip.Location = new System.Drawing.Point(0, 0);
|
||||
this.MenuStrip.Name = "MenuStrip";
|
||||
this.MenuStrip.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
|
||||
@@ -274,7 +272,7 @@ namespace Netch.Forms
|
||||
//
|
||||
this.UninstallServiceToolStripMenuItem.Name = "UninstallServiceToolStripMenuItem";
|
||||
this.UninstallServiceToolStripMenuItem.Size = new System.Drawing.Size(219, 22);
|
||||
this.UninstallServiceToolStripMenuItem.Text = "Uninstall Service";
|
||||
this.UninstallServiceToolStripMenuItem.Text = "Uninstall NF Service";
|
||||
this.UninstallServiceToolStripMenuItem.Click += new System.EventHandler(this.UninstallServiceToolStripMenuItem_Click);
|
||||
//
|
||||
// reinstallTapDriverToolStripMenuItem
|
||||
@@ -313,15 +311,6 @@ namespace Netch.Forms
|
||||
this.exitToolStripMenuItem.Text = "Exit";
|
||||
this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
|
||||
//
|
||||
// RelyToolStripMenuItem
|
||||
//
|
||||
this.RelyToolStripMenuItem.BackColor = System.Drawing.SystemColors.Control;
|
||||
this.RelyToolStripMenuItem.ForeColor = System.Drawing.Color.Red;
|
||||
this.RelyToolStripMenuItem.Name = "RelyToolStripMenuItem";
|
||||
this.RelyToolStripMenuItem.Size = new System.Drawing.Size(244, 22);
|
||||
this.RelyToolStripMenuItem.Text = "Unable to start? Click me to download";
|
||||
this.RelyToolStripMenuItem.Click += new System.EventHandler(this.RelyToolStripMenuItem_Click);
|
||||
//
|
||||
// ConfigurationGroupBox
|
||||
//
|
||||
this.ConfigurationGroupBox.Controls.Add(this.configLayoutPanel);
|
||||
@@ -406,6 +395,7 @@ namespace Netch.Forms
|
||||
this.ModeComboBox.Size = new System.Drawing.Size(546, 24);
|
||||
this.ModeComboBox.TabIndex = 2;
|
||||
this.ModeComboBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.ComboBox_DrawItem);
|
||||
this.ModeComboBox.SelectedIndexChanged += new System.EventHandler(this.ModeComboBox_SelectedIndexChanged);
|
||||
//
|
||||
// ServerComboBox
|
||||
//
|
||||
@@ -420,6 +410,7 @@ namespace Netch.Forms
|
||||
this.ServerComboBox.Size = new System.Drawing.Size(546, 24);
|
||||
this.ServerComboBox.TabIndex = 1;
|
||||
this.ServerComboBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.ComboBox_DrawItem);
|
||||
this.ServerComboBox.SelectedIndexChanged += new System.EventHandler(this.ServerComboBox_SelectedIndexChanged);
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
@@ -752,7 +743,6 @@ namespace Netch.Forms
|
||||
private System.Windows.Forms.TableLayoutPanel ProfileTable;
|
||||
private System.Windows.Forms.ToolStripMenuItem reinstallTapDriverToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem ReloadModesToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem RelyToolStripMenuItem;
|
||||
private System.Windows.Forms.ComboBox ServerComboBox;
|
||||
private System.Windows.Forms.Label ServerLabel;
|
||||
private System.Windows.Forms.ToolStripMenuItem ServerToolStripMenuItem;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.ServiceProcess;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Controllers;
|
||||
@@ -10,10 +11,8 @@ using Netch.Forms.Mode;
|
||||
using Netch.Forms.Server;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
using nfapinet;
|
||||
using Trojan = Netch.Forms.Server.Trojan;
|
||||
using VMess = Netch.Forms.Server.VMess;
|
||||
using WebClient = Netch.Override.WebClient;
|
||||
|
||||
namespace Netch.Forms
|
||||
{
|
||||
@@ -40,7 +39,7 @@ namespace Netch.Forms
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Import servers error!"), info: false);
|
||||
MessageBoxX.Show(i18N.Translate("Import servers error!"), LogLevel.ERROR);
|
||||
}
|
||||
|
||||
InitServer();
|
||||
@@ -88,6 +87,28 @@ namespace Netch.Forms
|
||||
Hide();
|
||||
}
|
||||
|
||||
private async void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Enabled = false;
|
||||
try
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
SaveConfigs();
|
||||
InitMode();
|
||||
});
|
||||
NotifyTip(i18N.Translate("Modes have been reload"));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
finally
|
||||
{
|
||||
Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 订阅
|
||||
@@ -98,133 +119,104 @@ namespace Netch.Forms
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void UpdateServersFromSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
private async void UpdateServersFromSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var bak_State = State;
|
||||
var bak_StateText = StatusLabel.Text;
|
||||
void DisableItems(bool v)
|
||||
{
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ProfileGroupBox.Enabled = ControlButton.Enabled = v;
|
||||
}
|
||||
|
||||
if (Global.Settings.UseProxyToUpdateSubscription && ServerComboBox.SelectedIndex == -1)
|
||||
Global.Settings.UseProxyToUpdateSubscription = false;
|
||||
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
if (Global.Settings.UseProxyToUpdateSubscription && ServerComboBox.SelectedIndex == -1)
|
||||
{
|
||||
// 当前 ServerComboBox 中至少有一项
|
||||
if (ServerComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||||
return;
|
||||
}
|
||||
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ControlButton.Enabled = SettingsButton.Enabled = false;
|
||||
ControlButton.Text = "...";
|
||||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Global.Settings.SubscribeLink.Count > 0)
|
||||
{
|
||||
StatusText(i18N.Translate("Starting update subscription"));
|
||||
DeleteServerPictureBox.Enabled = false;
|
||||
|
||||
UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled = false;
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
{
|
||||
var mode = new Models.Mode
|
||||
{
|
||||
Remark = "ProxyUpdate",
|
||||
Type = 5
|
||||
};
|
||||
MainController = new MainController();
|
||||
MainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
}
|
||||
|
||||
foreach (var item in Global.Settings.SubscribeLink)
|
||||
{
|
||||
using var client = new WebClient();
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(item.UserAgent))
|
||||
{
|
||||
client.Headers.Add("User-Agent", item.UserAgent);
|
||||
}
|
||||
else
|
||||
{
|
||||
client.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36");
|
||||
}
|
||||
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
{
|
||||
client.Proxy = new WebProxy($"http://127.0.0.1:{Global.Settings.HTTPLocalPort}");
|
||||
}
|
||||
|
||||
var response = client.DownloadString(item.Link);
|
||||
|
||||
try
|
||||
{
|
||||
response = ShareLink.URLSafeBase64Decode(response);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
Global.Settings.Server = Global.Settings.Server.Where(server => server.Group != item.Remark).ToList();
|
||||
var result = ShareLink.Parse(response);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
foreach (var x in result)
|
||||
{
|
||||
x.Group = item.Remark;
|
||||
}
|
||||
|
||||
Global.Settings.Server.AddRange(result);
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
UpdateChecker.Name,
|
||||
string.Format(i18N.Translate("Update {1} server(s) from {0}"), item.Remark, result.Count),
|
||||
ToolTipIcon.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
UpdateChecker.Name,
|
||||
string.Format(i18N.Translate("Update servers error from {0}"), item.Remark),
|
||||
ToolTipIcon.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
InitServer();
|
||||
DeleteServerPictureBox.Enabled = true;
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
{
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ControlButton.Enabled = SettingsButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Start");
|
||||
MainController.Stop();
|
||||
NatTypeStatusLabel.Text = "";
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
StatusText(i18N.Translate("Subscription updated"));
|
||||
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ControlButton.Enabled = SettingsButton.Enabled = true;
|
||||
UpdateStatus(bak_State);
|
||||
StatusLabel.Text = bak_StateText;
|
||||
|
||||
}).ContinueWith(task => { BeginInvoke(new Action(() => { UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled = true; })); });
|
||||
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
UpdateChecker.Name,
|
||||
i18N.Translate("Updating in the background"),
|
||||
ToolTipIcon.Info);
|
||||
}
|
||||
else
|
||||
if (Global.Settings.SubscribeLink.Count <= 0)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("No subscription link"));
|
||||
return;
|
||||
}
|
||||
|
||||
StatusText(i18N.Translate("Starting update subscription"));
|
||||
DisableItems(false);
|
||||
try
|
||||
{
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
{
|
||||
var mode = new Models.Mode
|
||||
{
|
||||
Remark = "ProxyUpdate",
|
||||
Type = 5
|
||||
};
|
||||
await _mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
}
|
||||
|
||||
var serverLock = new object();
|
||||
|
||||
await Task.WhenAll(Global.Settings.SubscribeLink.Select(async item => await Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = WebUtil.CreateRequest(item.Link);
|
||||
|
||||
if (!string.IsNullOrEmpty(item.UserAgent)) request.UserAgent = item.UserAgent;
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
request.Proxy = new WebProxy($"http://127.0.0.1:{Global.Settings.HTTPLocalPort}");
|
||||
|
||||
var str = await WebUtil.DownloadStringAsync(request);
|
||||
|
||||
try
|
||||
{
|
||||
str = ShareLink.URLSafeBase64Decode(str);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
lock (serverLock)
|
||||
{
|
||||
Global.Settings.Server = Global.Settings.Server.Where(server => server.Group != item.Remark).ToList();
|
||||
var result = ShareLink.Parse(str);
|
||||
if (result != null)
|
||||
{
|
||||
foreach (var x in result) x.Group = item.Remark;
|
||||
|
||||
Global.Settings.Server.AddRange(result);
|
||||
NotifyTip(i18N.TranslateFormat("Update {1} server(s) from {0}", item.Remark, result.Count));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (WebException e)
|
||||
{
|
||||
NotifyTip($"{i18N.TranslateFormat("Update servers error from {0}", item.Remark)}\n{e.Message}", info: false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error(e.ToString());
|
||||
}
|
||||
})).ToArray());
|
||||
|
||||
Configuration.Save();
|
||||
await Task.Run(InitServer);
|
||||
StatusText(i18N.Translate("Subscription updated"));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
{
|
||||
await _mainController.Stop();
|
||||
}
|
||||
|
||||
DisableItems(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,180 +224,130 @@ namespace Netch.Forms
|
||||
|
||||
#region 选项
|
||||
|
||||
private void RestartServiceToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Enabled = false;
|
||||
StatusText(i18N.Translate("Restarting service"));
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var service = new ServiceController("netfilter2");
|
||||
if (service.Status == ServiceControllerStatus.Stopped)
|
||||
{
|
||||
service.Start();
|
||||
service.WaitForStatus(ServiceControllerStatus.Running);
|
||||
}
|
||||
else if (service.Status == ServiceControllerStatus.Running)
|
||||
{
|
||||
service.Stop();
|
||||
service.WaitForStatus(ServiceControllerStatus.Stopped);
|
||||
service.Start();
|
||||
service.WaitForStatus(ServiceControllerStatus.Running);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
NFAPI.nf_registerDriver("netfilter2");
|
||||
}
|
||||
|
||||
MessageBoxX.Show(i18N.Translate("Service has been restarted"), owner: this);
|
||||
Enabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
private void UninstallServiceToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Enabled = false;
|
||||
StatusText(i18N.Translate("Uninstalling Service"));
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (NFController.UninstallDriver())
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Service has been uninstalled"), owner: this);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show(i18N.Translate("Error", e.Message));
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
|
||||
StatusText(i18N.Translate(StateExtension.GetStatusString(State.Waiting)));
|
||||
Enabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
private void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Enabled = false;
|
||||
SaveConfigs();
|
||||
Task.Run(() =>
|
||||
{
|
||||
InitMode();
|
||||
|
||||
MessageBoxX.Show(i18N.Translate("Modes have been reload"), owner: this);
|
||||
Enabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
private void CleanDNSCacheToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var bak_State = State;
|
||||
var bak_StateText = StatusLabel.Text;
|
||||
|
||||
try
|
||||
{
|
||||
Enabled = false;
|
||||
DNS.Cache.Clear();
|
||||
|
||||
MessageBoxX.Show(i18N.Translate("DNS cache cleanup succeeded"), owner: this);
|
||||
StatusText(i18N.Translate("DNS cache cleanup succeeded"));
|
||||
Enabled = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
UpdateStatus(bak_State);
|
||||
StatusLabel.Text = bak_StateText;
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Utils.Utils.OpenDir(@".\");
|
||||
Utils.Utils.Open(".\\");
|
||||
}
|
||||
|
||||
private void reinstallTapDriverToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
private async void CleanDNSCacheToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Task.Run(() =>
|
||||
try
|
||||
{
|
||||
StatusText(i18N.Translate("Reinstalling TUN/TAP driver"));
|
||||
Enabled = false;
|
||||
try
|
||||
{
|
||||
Configuration.deltapall();
|
||||
Configuration.addtap();
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
UpdateChecker.Name, i18N.Translate("Reinstall TUN/TAP driver successfully"),
|
||||
ToolTipIcon.Info);
|
||||
}
|
||||
catch
|
||||
{
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
UpdateChecker.Name, i18N.Translate("Reinstall TUN/TAP driver failed"),
|
||||
ToolTipIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
UpdateStatus(State.Waiting);
|
||||
Enabled = true;
|
||||
}
|
||||
});
|
||||
await Task.Run(() => DNS.Cache.Clear());
|
||||
StatusText(i18N.Translate("DNS cache cleanup succeeded"));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private void updateACLWithProxyToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
updateACLWithProxyToolStripMenuItem.Enabled = false;
|
||||
UpdateACL(true);
|
||||
}
|
||||
|
||||
// 当前 ServerComboBox 中至少有一项
|
||||
if (ServerComboBox.SelectedIndex == -1)
|
||||
private void updateACLToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
UpdateACL(false);
|
||||
}
|
||||
|
||||
private async void UpdateACL(bool useProxy)
|
||||
{
|
||||
void DisableItems(bool v)
|
||||
{
|
||||
UpdateACLToolStripMenuItem.Enabled = updateACLWithProxyToolStripMenuItem.Enabled = v;
|
||||
}
|
||||
|
||||
if (useProxy && ServerComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||||
return;
|
||||
}
|
||||
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ControlButton.Enabled = SettingsButton.Enabled = false;
|
||||
ControlButton.Text = "...";
|
||||
DisableItems(false);
|
||||
|
||||
|
||||
Task.Run(() =>
|
||||
NotifyTip(i18N.Translate("Updating in the background"));
|
||||
try
|
||||
{
|
||||
var mode = new Models.Mode
|
||||
if (useProxy)
|
||||
{
|
||||
Remark = "ProxyUpdate",
|
||||
Type = 5
|
||||
};
|
||||
MainController = new MainController();
|
||||
MainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
|
||||
using var client = new WebClient();
|
||||
|
||||
client.Proxy = new WebProxy($"http://127.0.0.1:{Global.Settings.HTTPLocalPort}");
|
||||
|
||||
StatusText(i18N.Translate("Updating in the background"));
|
||||
try
|
||||
{
|
||||
client.DownloadFile(Global.Settings.ACL, "bin\\default.acl");
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
UpdateChecker.Name, i18N.Translate("ACL updated successfully"),
|
||||
ToolTipIcon.Info);
|
||||
var mode = new Models.Mode
|
||||
{
|
||||
Remark = "ProxyUpdate",
|
||||
Type = 5
|
||||
};
|
||||
State = State.Starting;
|
||||
await _mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
var req = WebUtil.CreateRequest(Global.Settings.ACL);
|
||||
if (useProxy)
|
||||
req.Proxy = new WebProxy($"http://127.0.0.1:{Global.Settings.HTTPLocalPort}");
|
||||
|
||||
await WebUtil.DownloadFileAsync(req, Path.Combine(Global.NetchDir, "bin\\default.acl"));
|
||||
NotifyTip(i18N.Translate("ACL updated successfully"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
NotifyTip(i18N.Translate("ACL update failed") + "\n" + e.Message, info: false);
|
||||
Logging.Error("更新 ACL 失败!" + e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (useProxy)
|
||||
{
|
||||
Logging.Error("使用代理更新 ACL 失败!" + e);
|
||||
MessageBoxX.Show(i18N.Translate("ACL update failed") + "\n" + e);
|
||||
await _mainController.Stop();
|
||||
State = State.Stopped;
|
||||
}
|
||||
finally
|
||||
|
||||
DisableItems(true);
|
||||
}
|
||||
}
|
||||
|
||||
private async void UninstallServiceToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Enabled = false;
|
||||
StatusText(i18N.Translate("Uninstalling NF Service"));
|
||||
try
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
UpdateStatus(State.Waiting);
|
||||
MainController.Stop();
|
||||
}
|
||||
});
|
||||
if (NFController.UninstallDriver())
|
||||
{
|
||||
StatusText(i18N.Translate("Service has been uninstalled"));
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async void reinstallTapDriverToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
StatusText(i18N.Translate("Reinstalling TUN/TAP driver"));
|
||||
Enabled = false;
|
||||
try
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Configuration.deltapall();
|
||||
Configuration.addtap();
|
||||
});
|
||||
StatusText(i18N.Translate("Reinstall TUN/TAP driver successfully"));
|
||||
}
|
||||
catch
|
||||
{
|
||||
NotifyTip(i18N.Translate("Reinstall TUN/TAP driver failed"), info: false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
State = State.Waiting;
|
||||
Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -416,14 +358,9 @@ namespace Netch.Forms
|
||||
Exit(true);
|
||||
}
|
||||
|
||||
private void RelyToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
System.Diagnostics.Process.Start("https://mega.nz/file/9OQ1EazJ#0pjJ3xt57AVLr29vYEEv15GSACtXVQOGlEOPpi_2Ico");
|
||||
}
|
||||
|
||||
private void VersionLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
System.Diagnostics.Process.Start($"https://github.com/{UpdateChecker.Owner}/{UpdateChecker.Repo}/releases");
|
||||
Utils.Utils.Open($"https://github.com/{UpdateChecker.Owner}/{UpdateChecker.Repo}/releases");
|
||||
}
|
||||
|
||||
private void AboutToolStripButton_Click(object sender, EventArgs e)
|
||||
@@ -432,39 +369,6 @@ namespace Netch.Forms
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void updateACLToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var bak_State = State;
|
||||
var bak_StateText = StatusLabel.Text;
|
||||
|
||||
StatusText(i18N.Translate("Starting update ACL"));
|
||||
using var client = new WebClient();
|
||||
|
||||
client.DownloadFileTaskAsync(Global.Settings.ACL, "bin\\default.acl");
|
||||
client.DownloadFileCompleted += ((sender, args) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (args.Error == null)
|
||||
{
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
UpdateChecker.Name, i18N.Translate("ACL updated successfully"),
|
||||
ToolTipIcon.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Error("ACL 更新失败!" + args.Error);
|
||||
MessageBoxX.Show(i18N.Translate("ACL update failed") + "\n" + args.Error);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
UpdateStatus(bak_State);
|
||||
StatusLabel.Text = bak_StateText;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Forms;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Controllers;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -11,22 +11,18 @@ namespace Netch.Forms
|
||||
/// <summary lang="zh">
|
||||
/// 此类用于禁用设计器
|
||||
/// </summary>
|
||||
[DesignerCategory("")] public partial class Dummy { }
|
||||
[DesignerCategory("")]
|
||||
public partial class Dummy
|
||||
{
|
||||
}
|
||||
|
||||
partial class MainForm
|
||||
{
|
||||
private void CheckUpdate()
|
||||
{
|
||||
var updater = new UpdateChecker();
|
||||
updater.NewVersionFound += (o, args) =>
|
||||
{
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
UpdateChecker.Name,
|
||||
$"{i18N.Translate(@"New version available",": ")}{updater.LatestVersionNumber}",
|
||||
ToolTipIcon.Info);
|
||||
};
|
||||
updater.Check(false, false);
|
||||
updater.NewVersionFound += (o, args) => { NotifyTip($"{i18N.Translate(@"New version available", ": ")}{updater.LatestVersionNumber}"); };
|
||||
updater.Check(false, Global.Settings.CheckBetaUpdate);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -9,22 +9,24 @@ using Netch.Utils;
|
||||
|
||||
namespace Netch.Forms
|
||||
{
|
||||
public partial class Dummy { }
|
||||
public partial class Dummy
|
||||
{
|
||||
}
|
||||
|
||||
partial class MainForm
|
||||
{
|
||||
|
||||
/// init at <see cref="MainForm_Load"/>
|
||||
private int _sizeHeight;
|
||||
private int _controlHeight;
|
||||
private int _profileBoxHeight;
|
||||
|
||||
private int _profileConfigurationHeight;
|
||||
private int _profileGroupboxHeight;
|
||||
private int _configurationGroupBoxHeight;
|
||||
|
||||
private void InitProfile()
|
||||
{
|
||||
// Clear
|
||||
foreach (var button in ProfileButtons)
|
||||
{
|
||||
button.Dispose();
|
||||
}
|
||||
|
||||
ProfileButtons.Clear();
|
||||
ProfileTable.ColumnStyles.Clear();
|
||||
@@ -33,51 +35,51 @@ namespace Netch.Forms
|
||||
var numProfile = Global.Settings.ProfileCount;
|
||||
if (numProfile == 0)
|
||||
{
|
||||
// Hide Profile GroupBox, Change window size
|
||||
configLayoutPanel.RowStyles[2].SizeType = SizeType.Percent;
|
||||
configLayoutPanel.RowStyles[2].Height = 0;
|
||||
ProfileGroupBox.Visible = false;
|
||||
|
||||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight - _controlHeight);
|
||||
Size = new Size(Size.Width, _sizeHeight - (_controlHeight + _profileBoxHeight));
|
||||
|
||||
return;
|
||||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight - _profileConfigurationHeight);
|
||||
Size = new Size(Size.Width, _sizeHeight - (_profileConfigurationHeight + _profileGroupboxHeight));
|
||||
}
|
||||
|
||||
configLayoutPanel.RowStyles[2].SizeType = SizeType.AutoSize;
|
||||
ProfileGroupBox.Visible = true;
|
||||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight);
|
||||
Size = new Size(Size.Width, _sizeHeight);
|
||||
|
||||
|
||||
ProfileTable.ColumnCount = numProfile;
|
||||
|
||||
while (Global.Settings.Profiles.Count < numProfile)
|
||||
else
|
||||
{
|
||||
Global.Settings.Profiles.Add(new Profile());
|
||||
}
|
||||
// Load Profiles
|
||||
ProfileTable.ColumnCount = numProfile;
|
||||
|
||||
// buttons
|
||||
for (var i = 0; i < numProfile; ++i)
|
||||
{
|
||||
var b = new Button();
|
||||
ProfileTable.Controls.Add(b, i, 0);
|
||||
b.Location = new Point(i * 100, 0);
|
||||
b.Click += ProfileButton_Click;
|
||||
b.Dock = DockStyle.Fill;
|
||||
b.Text = !Global.Settings.Profiles[i].IsDummy ? Global.Settings.Profiles[i].ProfileName : i18N.Translate("None");
|
||||
while (Global.Settings.Profiles.Count < numProfile)
|
||||
{
|
||||
Global.Settings.Profiles.Add(new Profile());
|
||||
}
|
||||
|
||||
ProfileButtons.Add(b);
|
||||
}
|
||||
for (var i = 0; i < numProfile; ++i)
|
||||
{
|
||||
var b = new Button();
|
||||
b.Click += ProfileButton_Click;
|
||||
b.Dock = DockStyle.Fill;
|
||||
b.Text = !Global.Settings.Profiles[i].IsDummy ? Global.Settings.Profiles[i].ProfileName : i18N.Translate("None");
|
||||
|
||||
// equal column
|
||||
for (var i = 1; i <= ProfileTable.RowCount; i++)
|
||||
{
|
||||
ProfileTable.RowStyles.Add(new RowStyle(SizeType.Percent, 1));
|
||||
}
|
||||
ProfileTable.Controls.Add(b, i, 0);
|
||||
ProfileButtons.Add(b);
|
||||
}
|
||||
|
||||
for (var i = 1; i <= ProfileTable.ColumnCount; i++)
|
||||
{
|
||||
ProfileTable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 1));
|
||||
// equal column
|
||||
for (var i = 1; i <= ProfileTable.RowCount; i++)
|
||||
{
|
||||
ProfileTable.RowStyles.Add(new RowStyle(SizeType.Percent, 1));
|
||||
}
|
||||
|
||||
for (var i = 1; i <= ProfileTable.ColumnCount; i++)
|
||||
{
|
||||
ProfileTable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 1));
|
||||
}
|
||||
|
||||
if (Size.Height == _sizeHeight) return;
|
||||
configLayoutPanel.RowStyles[2].SizeType = SizeType.AutoSize;
|
||||
ProfileGroupBox.Visible = true;
|
||||
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight);
|
||||
Size = new Size(Size.Width, _sizeHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,14 +131,18 @@ namespace Netch.Forms
|
||||
Global.Settings.Profiles[index] = new Profile(selectedServer, selectedMode, name);
|
||||
}
|
||||
|
||||
public List<Button> ProfileButtons = new List<Button>();
|
||||
private void RemoveProfile(int index)
|
||||
{
|
||||
Global.Settings.Profiles[index] = new Profile();
|
||||
}
|
||||
|
||||
private void ProfileButton_Click(object sender, EventArgs e)
|
||||
|
||||
private List<Button> ProfileButtons = new List<Button>();
|
||||
|
||||
private async void ProfileButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
var index = ProfileButtons.IndexOf((Button) sender);
|
||||
|
||||
//Utils.Logging.Info(String.Format("Button no.{0} clicked", index));
|
||||
|
||||
if (ModifierKeys == Keys.Control)
|
||||
{
|
||||
if (ServerComboBox.SelectedIndex == -1)
|
||||
@@ -145,7 +151,7 @@ namespace Netch.Forms
|
||||
}
|
||||
else if (ModeComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select an mode first"));
|
||||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||||
}
|
||||
else if (ProfileNameText.Text == "")
|
||||
{
|
||||
@@ -156,51 +162,48 @@ namespace Netch.Forms
|
||||
SaveProfile(index);
|
||||
ProfileButtons[index].Text = ProfileNameText.Text;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (Global.Settings.Profiles[index].IsDummy)
|
||||
{
|
||||
if (ProfileButtons[index].Text == i18N.Translate("Error") || ProfileButtons[index].Text == i18N.Translate("None"))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("No saved profile here. Save a profile first by Ctrl+Click on the button"));
|
||||
}
|
||||
MessageBoxX.Show(i18N.Translate("No saved profile here. Save a profile first by Ctrl+Click on the button"));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProfileNameText.Text = LoadProfile(index);
|
||||
if (ModifierKeys == Keys.Shift)
|
||||
{
|
||||
if (MessageBoxX.Show(i18N.Translate("Remove this Profile?"), confirm: true) != DialogResult.OK) return;
|
||||
RemoveProfile(index);
|
||||
ProfileButtons[index].Text = i18N.Translate("None");
|
||||
// MessageBoxX.Show(i18N.Translate("Profile Removed!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// start the profile
|
||||
var need2ndStart = true;
|
||||
if (State == State.Waiting || State == State.Stopped)
|
||||
try
|
||||
{
|
||||
ProfileNameText.Text = LoadProfile(index);
|
||||
|
||||
// start the profile
|
||||
ControlFun();
|
||||
if (State == State.Stopping || State == State.Stopped)
|
||||
{
|
||||
while (State != State.Stopped)
|
||||
{
|
||||
need2ndStart = false;
|
||||
await Task.Delay(250);
|
||||
}
|
||||
|
||||
ControlButton.PerformClick();
|
||||
|
||||
if (need2ndStart)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
while (State != State.Stopped)
|
||||
{
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
|
||||
ControlButton.PerformClick();
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
Logging.Info(ee.Message);
|
||||
ProfileButtons[index].Text = i18N.Translate("Error");
|
||||
Thread.Sleep(1200);
|
||||
ProfileButtons[index].Text = i18N.Translate("None");
|
||||
});
|
||||
ControlFun();
|
||||
}
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
Logging.Info(ee.ToString());
|
||||
ProfileButtons[index].Text = i18N.Translate("Error");
|
||||
await Task.Delay(1200);
|
||||
ProfileButtons[index].Text = i18N.Translate("None");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Netch.Forms
|
||||
{
|
||||
#region Server
|
||||
|
||||
public void TestServer()
|
||||
private static void TestServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -24,6 +24,7 @@ namespace Netch.Forms
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +52,7 @@ namespace Netch.Forms
|
||||
|
||||
#region Mode
|
||||
|
||||
public void InitMode()
|
||||
private void InitMode()
|
||||
{
|
||||
ModeComboBox.Items.Clear();
|
||||
Global.ModeFiles.Clear();
|
||||
@@ -139,7 +140,7 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectLastMode()
|
||||
private void SelectLastMode()
|
||||
{
|
||||
// 如果值合法,选中该位置
|
||||
if (Global.Settings.ModeComboBoxSelectedIndex > 0 &&
|
||||
@@ -182,7 +183,7 @@ namespace Netch.Forms
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Init at MainForm_Load()
|
||||
/// Init at <see cref="MainForm_Load"/>
|
||||
/// </summary>
|
||||
private int _eWidth;
|
||||
|
||||
@@ -205,32 +206,18 @@ namespace Netch.Forms
|
||||
|
||||
switch (cbx.Items[e.Index])
|
||||
{
|
||||
case Models.Server _:
|
||||
case Models.Server item:
|
||||
{
|
||||
var item = cbx.Items[e.Index] as Models.Server;
|
||||
|
||||
// 计算延迟底色
|
||||
SolidBrush brush;
|
||||
if (item.Delay > 200)
|
||||
{
|
||||
// 红色
|
||||
brush = new SolidBrush(Color.Red);
|
||||
}
|
||||
else if (item.Delay > 80)
|
||||
{
|
||||
// 黄色
|
||||
brush = new SolidBrush(Color.Yellow);
|
||||
}
|
||||
else if (item.Delay >= 0)
|
||||
{
|
||||
// 绿色
|
||||
brush = new SolidBrush(Color.FromArgb(50, 255, 56));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 灰色
|
||||
brush = new SolidBrush(Color.Gray);
|
||||
}
|
||||
|
||||
// 绘制延迟底色
|
||||
e.Graphics.FillRectangle(brush, _eWidth * 9, e.Bounds.Y, _eWidth, e.Bounds.Height);
|
||||
@@ -240,10 +227,8 @@ namespace Netch.Forms
|
||||
_eWidth * 9 + _eWidth / 30, e.Bounds.Y);
|
||||
break;
|
||||
}
|
||||
case Models.Mode _:
|
||||
case Models.Mode item:
|
||||
{
|
||||
var item = cbx.Items[e.Index] as Models.Mode;
|
||||
|
||||
// 绘制 模式Box 底色
|
||||
e.Graphics.FillRectangle(new SolidBrush(Color.Gray), _eWidth * 9, e.Bounds.Y, _eWidth,
|
||||
e.Bounds.Height);
|
||||
@@ -257,6 +242,7 @@ namespace Netch.Forms
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -11,13 +12,101 @@ namespace Netch.Forms
|
||||
|
||||
partial class MainForm
|
||||
{
|
||||
private State _state = State.Waiting;
|
||||
|
||||
/// <summary>
|
||||
/// 当前状态
|
||||
/// </summary>
|
||||
public State State { get; private set; } = State.Waiting;
|
||||
|
||||
public void NatTypeStatusText(string text = "",string Country = "")
|
||||
public State State
|
||||
{
|
||||
get => _state;
|
||||
private set
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
// TODO:使所有 State 赋值不在线程中执行然后移除此代码块
|
||||
BeginInvoke(new Action(() => { State = value; }));
|
||||
return;
|
||||
}
|
||||
|
||||
void StartDisableItems(bool enabled)
|
||||
{
|
||||
ServerComboBox.Enabled =
|
||||
ModeComboBox.Enabled =
|
||||
EditModePictureBox.Enabled =
|
||||
EditServerPictureBox.Enabled =
|
||||
DeleteModePictureBox.Enabled =
|
||||
DeleteServerPictureBox.Enabled = enabled;
|
||||
|
||||
// 启动需要禁用的控件
|
||||
UninstallServiceToolStripMenuItem.Enabled =
|
||||
updateACLWithProxyToolStripMenuItem.Enabled =
|
||||
UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled =
|
||||
reinstallTapDriverToolStripMenuItem.Enabled =
|
||||
ReloadModesToolStripMenuItem.Enabled = enabled;
|
||||
}
|
||||
|
||||
_state = value;
|
||||
|
||||
StatusText(i18N.Translate(StateExtension.GetStatusString(value)));
|
||||
switch (value)
|
||||
{
|
||||
case State.Waiting:
|
||||
ControlButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Start");
|
||||
|
||||
break;
|
||||
case State.Starting:
|
||||
ControlButton.Enabled = false;
|
||||
ControlButton.Text = "...";
|
||||
|
||||
ProfileGroupBox.Enabled = false;
|
||||
StartDisableItems(false);
|
||||
break;
|
||||
case State.Started:
|
||||
ControlButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Stop");
|
||||
|
||||
StatusTextAppend(_mainController.PortInfo);
|
||||
|
||||
ProfileGroupBox.Enabled = true;
|
||||
|
||||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = true;
|
||||
break;
|
||||
case State.Stopping:
|
||||
ControlButton.Enabled = false;
|
||||
ControlButton.Text = "...";
|
||||
|
||||
ProfileGroupBox.Enabled = false;
|
||||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = false;
|
||||
NatTypeStatusText();
|
||||
break;
|
||||
case State.Stopped:
|
||||
ControlButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Start");
|
||||
|
||||
LastUploadBandwidth = 0;
|
||||
LastDownloadBandwidth = 0;
|
||||
|
||||
ProfileGroupBox.Enabled = true;
|
||||
StartDisableItems(true);
|
||||
break;
|
||||
case State.Terminating:
|
||||
Dispose();
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void NatTypeStatusText(string text = "", string country = "")
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action<string, string>(NatTypeStatusText), text, country);
|
||||
return;
|
||||
}
|
||||
|
||||
if (State != State.Started)
|
||||
{
|
||||
NatTypeStatusLabel.Text = "";
|
||||
@@ -27,14 +116,15 @@ namespace Netch.Forms
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
if (Country != "")
|
||||
if (country != "")
|
||||
{
|
||||
NatTypeStatusLabel.Text = String.Format("NAT{0}{1}[{2}]", i18N.Translate(": "), text, Country);
|
||||
NatTypeStatusLabel.Text = String.Format("NAT{0}{1} [{2}]", i18N.Translate(": "), text, country);
|
||||
}
|
||||
else
|
||||
{
|
||||
NatTypeStatusLabel.Text = String.Format("NAT{0}{1}", i18N.Translate(": "), text);
|
||||
}
|
||||
|
||||
if (Enum.TryParse(text, false, out STUN_Client.NatType natType))
|
||||
{
|
||||
NatTypeStatusLightLabel.Visible = true;
|
||||
@@ -43,12 +133,16 @@ namespace Netch.Forms
|
||||
}
|
||||
else
|
||||
{
|
||||
NatTypeStatusLabel.Text = "NAT" + i18N.Translate(": ") + i18N.Translate("Test failed");
|
||||
NatTypeStatusLabel.Text = $@"NAT{i18N.Translate(": ", "Test failed")}";
|
||||
}
|
||||
|
||||
NatTypeStatusLabel.Visible = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新 NAT指示灯颜色
|
||||
/// </summary>
|
||||
/// <param name="natType"></param>
|
||||
private void UpdateNatTypeLight(STUN_Client.NatType natType)
|
||||
{
|
||||
Color c;
|
||||
@@ -75,82 +169,24 @@ namespace Netch.Forms
|
||||
NatTypeStatusLightLabel.ForeColor = c;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 更新状态栏文本
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
public void StatusText(string text)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action<string>(StatusText), text);
|
||||
return;
|
||||
}
|
||||
|
||||
StatusLabel.Text = i18N.Translate("Status", ": ") + text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update UI, Status, Status Label
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
public void UpdateStatus(State state)
|
||||
public void StatusTextAppend(string text)
|
||||
{
|
||||
State = state;
|
||||
StatusText(i18N.Translate(StateExtension.GetStatusString(state)));
|
||||
// TODO 补充
|
||||
switch (state)
|
||||
{
|
||||
case State.Waiting:
|
||||
ControlButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Start");
|
||||
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ControlButton.Enabled = SettingsButton.Enabled = true;
|
||||
updateACLWithProxyToolStripMenuItem.Enabled = true;
|
||||
|
||||
NatTypeStatusText();
|
||||
break;
|
||||
case State.Starting:
|
||||
ControlButton.Enabled = false;
|
||||
ControlButton.Text = "...";
|
||||
|
||||
ServerComboBox.Enabled = false;
|
||||
ModeComboBox.Enabled = false;
|
||||
|
||||
UninstallServiceToolStripMenuItem.Enabled = false;
|
||||
updateACLWithProxyToolStripMenuItem.Enabled = false;
|
||||
UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled = false;
|
||||
reinstallTapDriverToolStripMenuItem.Enabled = false;
|
||||
break;
|
||||
case State.Started:
|
||||
ControlButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Stop");
|
||||
break;
|
||||
case State.Stopping:
|
||||
ControlButton.Enabled = false;
|
||||
ControlButton.Text = "...";
|
||||
|
||||
ProfileGroupBox.Enabled = false;
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = SettingsButton.Enabled = true;
|
||||
UsedBandwidthLabel.Visible = UploadSpeedLabel.Visible = DownloadSpeedLabel.Visible = false;
|
||||
NatTypeStatusText();
|
||||
break;
|
||||
case State.Stopped:
|
||||
ControlButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Start");
|
||||
|
||||
LastUploadBandwidth = 0;
|
||||
LastDownloadBandwidth = 0;
|
||||
|
||||
ServerComboBox.Enabled = true;
|
||||
ModeComboBox.Enabled = true;
|
||||
ProfileGroupBox.Enabled = true;
|
||||
|
||||
UninstallServiceToolStripMenuItem.Enabled = true;
|
||||
updateACLWithProxyToolStripMenuItem.Enabled = true;
|
||||
UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled = true;
|
||||
reinstallTapDriverToolStripMenuItem.Enabled = true;
|
||||
break;
|
||||
case State.Terminating:
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateStatus()
|
||||
{
|
||||
UpdateStatus(State);
|
||||
StatusLabel.Text += text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,18 +19,7 @@ namespace Netch.Forms
|
||||
/// <summary>
|
||||
/// 主控制器
|
||||
/// </summary>
|
||||
public MainController MainController;
|
||||
|
||||
/// <summary>
|
||||
/// 是否第一次打开
|
||||
/// </summary>
|
||||
public bool IsFirstOpened = true;
|
||||
|
||||
/// <summary>
|
||||
/// 主窗体的静态实例
|
||||
/// </summary>
|
||||
public static MainForm Instance;
|
||||
|
||||
private MainController _mainController = new MainController();
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
@@ -40,8 +29,6 @@ namespace Netch.Forms
|
||||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
||||
|
||||
CheckForIllegalCrossThreadCalls = false;
|
||||
// MenuStrip.Renderer = new Override.ToolStripProfessionalRender();
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
private void SaveConfigs()
|
||||
@@ -64,6 +51,10 @@ namespace Netch.Forms
|
||||
|
||||
private void MainForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
OnlyInstance.Called += OnCalled;
|
||||
// 计算 ComboBox绘制 目标宽度
|
||||
_eWidth = ServerComboBox.Width / 10;
|
||||
|
||||
// 加载服务器
|
||||
InitServer();
|
||||
|
||||
@@ -73,18 +64,21 @@ namespace Netch.Forms
|
||||
// 加载翻译
|
||||
InitText();
|
||||
|
||||
//
|
||||
// 隐藏 NatTypeStatusLabel
|
||||
NatTypeStatusText();
|
||||
|
||||
// 加载快速配置
|
||||
_sizeHeight = Size.Height;
|
||||
_controlHeight = ConfigurationGroupBox.Controls[0].Height / 3;
|
||||
_profileBoxHeight = ProfileGroupBox.Height;
|
||||
_configurationGroupBoxHeight = ConfigurationGroupBox.Height;
|
||||
_profileConfigurationHeight = ConfigurationGroupBox.Controls[0].Height / 3; // 因为 AutoSize, 所以得到的是Controls的总高度
|
||||
_profileGroupboxHeight = ProfileGroupBox.Height;
|
||||
// 加载快速配置
|
||||
InitProfile();
|
||||
|
||||
// 为 ComboBox绘制 收集宽度数据
|
||||
_eWidth = ServerComboBox.Width / 10;
|
||||
// 打开软件时启动加速,产生开始按钮点击事件
|
||||
if (Global.Settings.StartWhenOpened)
|
||||
{
|
||||
ControlButton.PerformClick();
|
||||
}
|
||||
|
||||
// 自动检测延迟
|
||||
Task.Run(() =>
|
||||
@@ -104,16 +98,28 @@ namespace Netch.Forms
|
||||
}
|
||||
});
|
||||
|
||||
// 打开软件时启动加速,产生开始按钮点击事件
|
||||
if (Global.Settings.StartWhenOpened)
|
||||
Task.Run(() =>
|
||||
{
|
||||
ControlButton.PerformClick();
|
||||
}
|
||||
// 检查更新
|
||||
if (Global.Settings.CheckUpdateWhenOpened)
|
||||
{
|
||||
CheckUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 检查更新
|
||||
if (Global.Settings.CheckUpdateWhenOpened)
|
||||
private void OnCalled(object sender, OnlyInstance.Commands e)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
CheckUpdate();
|
||||
case OnlyInstance.Commands.Show:
|
||||
NotifyIcon_MouseDoubleClick(null, null);
|
||||
break;
|
||||
case OnlyInstance.Commands.Exit:
|
||||
Exit(true);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(e), e, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,17 +136,12 @@ namespace Netch.Forms
|
||||
{
|
||||
// 使关闭时窗口向右下角缩小的效果
|
||||
WindowState = FormWindowState.Minimized;
|
||||
NotifyIcon.Visible = true;
|
||||
|
||||
if (IsFirstOpened)
|
||||
if (_isFirstCloseWindow)
|
||||
{
|
||||
// 显示提示语
|
||||
NotifyIcon.ShowBalloonTip(5,
|
||||
UpdateChecker.Name,
|
||||
i18N.Translate("Netch is now minimized to the notification bar, double click this icon to restore."),
|
||||
ToolTipIcon.Info);
|
||||
|
||||
IsFirstOpened = false;
|
||||
NotifyTip(i18N.Translate("Netch is now minimized to the notification bar, double click this icon to restore."));
|
||||
_isFirstCloseWindow = false;
|
||||
}
|
||||
|
||||
Hide();
|
||||
@@ -155,8 +156,6 @@ namespace Netch.Forms
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
//防止模式选择框变成蓝色:D
|
||||
ModeComboBox.Select(0, 0);
|
||||
ControlFun();
|
||||
}
|
||||
|
||||
@@ -184,7 +183,7 @@ namespace Netch.Forms
|
||||
InitProfile();
|
||||
}
|
||||
|
||||
public void InitText()
|
||||
private void InitText()
|
||||
{
|
||||
ServerToolStripMenuItem.Text = i18N.Translate("Server");
|
||||
ImportServersFromClipboardToolStripMenuItem.Text = i18N.Translate("Import Servers From Clipboard");
|
||||
@@ -200,23 +199,22 @@ namespace Netch.Forms
|
||||
UpdateServersFromSubscribeLinksToolStripMenuItem.Text = i18N.Translate("Update Servers From Subscribe Links");
|
||||
OptionsToolStripMenuItem.Text = i18N.Translate("Options");
|
||||
ReloadModesToolStripMenuItem.Text = i18N.Translate("Reload Modes");
|
||||
UninstallServiceToolStripMenuItem.Text = i18N.Translate("Uninstall Service");
|
||||
UninstallServiceToolStripMenuItem.Text = i18N.Translate("Uninstall NF Service");
|
||||
CleanDNSCacheToolStripMenuItem.Text = i18N.Translate("Clean DNS Cache");
|
||||
UpdateACLToolStripMenuItem.Text = i18N.Translate("Update ACL");
|
||||
updateACLWithProxyToolStripMenuItem.Text = i18N.Translate("Update ACL with proxy");
|
||||
reinstallTapDriverToolStripMenuItem.Text = i18N.Translate("Reinstall TUN/TAP driver");
|
||||
OpenDirectoryToolStripMenuItem.Text = i18N.Translate("Open Directory");
|
||||
AboutToolStripButton.Text = i18N.Translate("About");
|
||||
VersionLabel.Text = i18N.Translate("xxx");
|
||||
// VersionLabel.Text = i18N.Translate("xxx");
|
||||
exitToolStripMenuItem.Text = i18N.Translate("Exit");
|
||||
RelyToolStripMenuItem.Text = i18N.Translate("Unable to start? Click me to download");
|
||||
ConfigurationGroupBox.Text = i18N.Translate("Configuration");
|
||||
ProfileLabel.Text = i18N.Translate("Profile");
|
||||
ModeLabel.Text = i18N.Translate("Mode");
|
||||
ServerLabel.Text = i18N.Translate("Server");
|
||||
UsedBandwidthLabel.Text = i18N.Translate("Used: 0 KB");
|
||||
DownloadSpeedLabel.Text = i18N.Translate("↓: 0 KB/s");
|
||||
UploadSpeedLabel.Text = i18N.Translate("↑: 0 KB/s");
|
||||
// UsedBandwidthLabel.Text = i18N.Translate("Used: 0 KB");
|
||||
// DownloadSpeedLabel.Text = i18N.Translate("↓: 0 KB/s");
|
||||
// UploadSpeedLabel.Text = i18N.Translate("↑: 0 KB/s");
|
||||
NotifyIcon.Text = i18N.Translate("Netch");
|
||||
ShowMainFormToolStripButton.Text = i18N.Translate("Show");
|
||||
ExitToolStripButton.Text = i18N.Translate("Exit");
|
||||
@@ -225,11 +223,42 @@ namespace Netch.Forms
|
||||
// 加载翻译
|
||||
|
||||
UsedBandwidthLabel.Text = $@"{i18N.Translate("Used", ": ")}0 KB";
|
||||
UpdateStatus();
|
||||
State = State;
|
||||
|
||||
VersionLabel.Text = UpdateChecker.Version;
|
||||
}
|
||||
|
||||
private void Exit(bool forceExit = false)
|
||||
{
|
||||
if (State != State.Waiting && State != State.Stopped && !Global.Settings.StopWhenExited && !forceExit)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
|
||||
|
||||
Visible = true;
|
||||
ShowInTaskbar = true; // 显示在系统任务栏
|
||||
WindowState = FormWindowState.Normal; // 还原窗体
|
||||
return;
|
||||
}
|
||||
|
||||
Hide();
|
||||
NotifyIcon.Visible = false;
|
||||
if (State != State.Waiting && State != State.Stopped)
|
||||
{
|
||||
// 已启动
|
||||
ControlFun();
|
||||
}
|
||||
|
||||
for (var i = 0; i < 16; i++)
|
||||
{
|
||||
if (State == State.Waiting || State == State.Stopped)
|
||||
break;
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
|
||||
SaveConfigs();
|
||||
State = State.Terminating;
|
||||
}
|
||||
|
||||
#region MISC
|
||||
|
||||
/// <summary>
|
||||
@@ -303,7 +332,6 @@ namespace Netch.Forms
|
||||
Enabled = true;
|
||||
StatusText(i18N.Translate("Test done"));
|
||||
Refresh();
|
||||
Configuration.Save();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -326,7 +354,7 @@ namespace Netch.Forms
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select an mode first"));
|
||||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +379,7 @@ namespace Netch.Forms
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select an mode first"));
|
||||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,6 +396,7 @@ namespace Netch.Forms
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -408,55 +437,11 @@ namespace Netch.Forms
|
||||
Visible = true;
|
||||
ShowInTaskbar = true; // 显示在系统任务栏
|
||||
WindowState = FormWindowState.Normal; // 还原窗体
|
||||
NotifyIcon.Visible = true; // 托盘图标隐藏
|
||||
}
|
||||
|
||||
Activate();
|
||||
}
|
||||
|
||||
private void Exit(bool forceExit = false)
|
||||
{
|
||||
// 已启动
|
||||
if (State != State.Waiting && State != State.Stopped)
|
||||
{
|
||||
if (forceExit)
|
||||
ControlFun();
|
||||
else
|
||||
{
|
||||
if (!Global.Settings.StopWhenExited)
|
||||
{
|
||||
// 未开启自动停止
|
||||
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
|
||||
|
||||
Visible = true;
|
||||
ShowInTaskbar = true; // 显示在系统任务栏
|
||||
WindowState = FormWindowState.Normal; // 还原窗体
|
||||
NotifyIcon.Visible = true; // 托盘图标隐藏
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotifyIcon.Visible = false;
|
||||
Hide();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
for (var i = 0; i < 16; i++)
|
||||
{
|
||||
if (State == State.Waiting || State == State.Stopped)
|
||||
break;
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
|
||||
SaveConfigs();
|
||||
UpdateStatus(State.Terminating);
|
||||
Dispose();
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
});
|
||||
}
|
||||
|
||||
private void ExitToolStripButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Exit();
|
||||
@@ -468,15 +453,33 @@ namespace Netch.Forms
|
||||
{
|
||||
Visible = true;
|
||||
ShowInTaskbar = true; //显示在系统任务栏
|
||||
WindowState = FormWindowState.Normal; //还原窗体
|
||||
NotifyIcon.Visible = true; //托盘图标隐藏
|
||||
WindowState = FormWindowState.Normal; //还原窗体
|
||||
}
|
||||
|
||||
Activate();
|
||||
}
|
||||
|
||||
private void NotifyTip(string text, int timeout = 0, bool info = true)
|
||||
{
|
||||
// 会阻塞线程 timeout 秒
|
||||
NotifyIcon.ShowBalloonTip(timeout,
|
||||
UpdateChecker.Name,
|
||||
text,
|
||||
info ? ToolTipIcon.Info : ToolTipIcon.Error);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
private void ModeComboBox_SelectedIndexChanged(object sender, EventArgs o)
|
||||
{
|
||||
Global.Settings.ModeComboBoxSelectedIndex = ModeComboBox.SelectedIndex;
|
||||
}
|
||||
|
||||
private void ServerComboBox_SelectedIndexChanged(object sender, EventArgs o)
|
||||
{
|
||||
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,15 +11,16 @@ namespace Netch.Forms.Mode
|
||||
{
|
||||
//用于判断当前窗口是否为编辑模式
|
||||
private bool EditMode;
|
||||
|
||||
//被编辑模式坐标
|
||||
private Models.Mode EditMode_Old;
|
||||
|
||||
/// <summary>
|
||||
/// 编辑模式
|
||||
/// </summary>
|
||||
/// <param name="mode">模式</param>
|
||||
/// 编辑模式
|
||||
/// </summary>
|
||||
/// <param name="mode">模式</param>
|
||||
public Process(Models.Mode mode)
|
||||
{
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
CheckForIllegalCrossThreadCalls = false;
|
||||
@@ -38,8 +39,8 @@ namespace Netch.Forms.Mode
|
||||
|
||||
FilenameTextBox.Text = mode.FileName;
|
||||
RemarkTextBox.Text = mode.Remark;
|
||||
|
||||
}
|
||||
|
||||
public Process()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -51,10 +52,10 @@ namespace Netch.Forms.Mode
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 扫描目录
|
||||
/// </summary>
|
||||
/// <param name="DirName">路径</param>
|
||||
public void ScanDirectory(string DirName)
|
||||
/// 扫描目录
|
||||
/// </summary>
|
||||
/// <param name="DirName">路径</param>
|
||||
public void ScanDirectory(string DirName)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -75,16 +76,24 @@ namespace Netch.Forms.Mode
|
||||
while (DirStack.Count > 0)
|
||||
{
|
||||
var DirInfo = new DirectoryInfo(DirStack.Pop());
|
||||
foreach (var DirChildInfo in DirInfo.GetDirectories())
|
||||
try
|
||||
{
|
||||
DirStack.Push(DirChildInfo.FullName);
|
||||
}
|
||||
foreach (var FileChildInfo in DirInfo.GetFiles())
|
||||
{
|
||||
if (FileChildInfo.Name.EndsWith(".exe") && !RuleListBox.Items.Contains(FileChildInfo.Name))
|
||||
foreach (var DirChildInfo in DirInfo.GetDirectories())
|
||||
{
|
||||
RuleListBox.Items.Add(FileChildInfo.Name);
|
||||
DirStack.Push(DirChildInfo.FullName);
|
||||
}
|
||||
|
||||
foreach (var FileChildInfo in DirInfo.GetFiles())
|
||||
{
|
||||
if (FileChildInfo.Name.EndsWith(".exe") && !RuleListBox.Items.Contains(FileChildInfo.Name))
|
||||
{
|
||||
RuleListBox.Items.Add(FileChildInfo.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,10 +145,11 @@ namespace Netch.Forms.Mode
|
||||
strip.Items.Add(i18N.Translate("Delete"));
|
||||
if (e.Button == MouseButtons.Right)
|
||||
{
|
||||
strip.Show(RuleListBox, e.Location);//鼠标右键按下弹出菜单
|
||||
strip.Show(RuleListBox, e.Location); //鼠标右键按下弹出菜单
|
||||
strip.MouseClick += deleteRule_Click;
|
||||
}
|
||||
}
|
||||
|
||||
void deleteRule_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (RuleListBox.SelectedIndex != -1)
|
||||
@@ -249,7 +259,7 @@ namespace Netch.Forms.Mode
|
||||
else
|
||||
{
|
||||
Global.Settings.ModeFileNameType = 2;
|
||||
FilenameTextBox.Text = ((long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds).ToString();
|
||||
FilenameTextBox.Text = ((long) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds).ToString();
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
@@ -261,6 +271,7 @@ namespace Netch.Forms.Mode
|
||||
MessageBoxX.Show(i18N.Translate("Please enter a mode filename"));
|
||||
return;
|
||||
}
|
||||
|
||||
var ModeFilename = Path.Combine("mode", FilenameTextBox.Text);
|
||||
|
||||
// 如果文件已存在,返回
|
||||
@@ -332,4 +343,4 @@ namespace Netch.Forms.Mode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
212
Netch/Forms/SettingForm.Designer.cs
generated
212
Netch/Forms/SettingForm.Designer.cs
generated
@@ -54,21 +54,22 @@
|
||||
this.BehaviorGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.LanguageLabel = new System.Windows.Forms.Label();
|
||||
this.LanguageComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.ModifySystemDNSCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.BootShadowsocksFromDLLCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.AclAddr = new System.Windows.Forms.TextBox();
|
||||
this.AclAddrTextBox = new System.Windows.Forms.TextBox();
|
||||
this.AclLabel = new System.Windows.Forms.Label();
|
||||
this.DetectionInterval_Label = new System.Windows.Forms.Label();
|
||||
this.DetectionInterval_TextBox = new System.Windows.Forms.TextBox();
|
||||
this.EnableStartedTcping_CheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.DelayTestAfterStartup_Label = new System.Windows.Forms.Label();
|
||||
this.DetectionIntervalLabel = new System.Windows.Forms.Label();
|
||||
this.DetectionIntervalTextBox = new System.Windows.Forms.TextBox();
|
||||
this.TcpingAtStartedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.STUN_ServerPortTextBox = new System.Windows.Forms.TextBox();
|
||||
this.STUNServerPortLabel = new System.Windows.Forms.Label();
|
||||
this.StunTextBoxSplitLabel = new System.Windows.Forms.Label();
|
||||
this.STUNServerLabel = new System.Windows.Forms.Label();
|
||||
this.RunAtStartup = new System.Windows.Forms.CheckBox();
|
||||
this.RunAtStartupCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.STUN_ServerTextBox = new System.Windows.Forms.TextBox();
|
||||
this.MinimizeWhenStartedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.ProfileCount_Label = new System.Windows.Forms.Label();
|
||||
this.ProfileCount_TextBox = new System.Windows.Forms.TextBox();
|
||||
this.ProfileCountLabel = new System.Windows.Forms.Label();
|
||||
this.ProfileCountTextBox = new System.Windows.Forms.TextBox();
|
||||
this.CheckBetaUpdateCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.CheckUpdateWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.StartWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.StopWhenExitedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
@@ -301,21 +302,22 @@
|
||||
//
|
||||
this.BehaviorGroupBox.Controls.Add(this.LanguageLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.LanguageComboBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.ModifySystemDNSCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.BootShadowsocksFromDLLCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.AclAddr);
|
||||
this.BehaviorGroupBox.Controls.Add(this.AclAddrTextBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.AclLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.DetectionInterval_Label);
|
||||
this.BehaviorGroupBox.Controls.Add(this.DetectionInterval_TextBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.EnableStartedTcping_CheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.DelayTestAfterStartup_Label);
|
||||
this.BehaviorGroupBox.Controls.Add(this.DetectionIntervalLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.DetectionIntervalTextBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.TcpingAtStartedCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.STUN_ServerPortTextBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.STUNServerPortLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.StunTextBoxSplitLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.STUNServerLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.RunAtStartup);
|
||||
this.BehaviorGroupBox.Controls.Add(this.RunAtStartupCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.STUN_ServerTextBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.MinimizeWhenStartedCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.ProfileCount_Label);
|
||||
this.BehaviorGroupBox.Controls.Add(this.ProfileCount_TextBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.ProfileCountLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.ProfileCountTextBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.CheckBetaUpdateCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.CheckUpdateWhenOpenedCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.StartWhenOpenedCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.StopWhenExitedCheckBox);
|
||||
@@ -345,23 +347,33 @@
|
||||
this.LanguageComboBox.Size = new System.Drawing.Size(121, 25);
|
||||
this.LanguageComboBox.TabIndex = 22;
|
||||
//
|
||||
// ModifySystemDNSCheckBox
|
||||
//
|
||||
this.ModifySystemDNSCheckBox.AutoSize = true;
|
||||
this.ModifySystemDNSCheckBox.Location = new System.Drawing.Point(12, 129);
|
||||
this.ModifySystemDNSCheckBox.Name = "ModifySystemDNSCheckBox";
|
||||
this.ModifySystemDNSCheckBox.Size = new System.Drawing.Size(143, 21);
|
||||
this.ModifySystemDNSCheckBox.TabIndex = 21;
|
||||
this.ModifySystemDNSCheckBox.Text = "Modify System DNS";
|
||||
this.ModifySystemDNSCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// BootShadowsocksFromDLLCheckBox
|
||||
//
|
||||
this.BootShadowsocksFromDLLCheckBox.AutoSize = true;
|
||||
this.BootShadowsocksFromDLLCheckBox.Location = new System.Drawing.Point(12, 102);
|
||||
this.BootShadowsocksFromDLLCheckBox.Name = "BootShadowsocksFromDLLCheckBox";
|
||||
this.BootShadowsocksFromDLLCheckBox.Size = new System.Drawing.Size(321, 21);
|
||||
this.BootShadowsocksFromDLLCheckBox.Size = new System.Drawing.Size(168, 21);
|
||||
this.BootShadowsocksFromDLLCheckBox.TabIndex = 21;
|
||||
this.BootShadowsocksFromDLLCheckBox.Text = "Start Shadowsocks from DLL (No support for ACL)";
|
||||
this.BootShadowsocksFromDLLCheckBox.Text = "SS DLL(No ACL support)";
|
||||
this.BootShadowsocksFromDLLCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// AclAddr
|
||||
// AclAddrTextBox
|
||||
//
|
||||
this.AclAddr.Location = new System.Drawing.Point(120, 273);
|
||||
this.AclAddr.Name = "AclAddr";
|
||||
this.AclAddr.Size = new System.Drawing.Size(315, 23);
|
||||
this.AclAddr.TabIndex = 19;
|
||||
this.AclAddr.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.AclAddrTextBox.Location = new System.Drawing.Point(120, 273);
|
||||
this.AclAddrTextBox.Name = "AclAddrTextBox";
|
||||
this.AclAddrTextBox.Size = new System.Drawing.Size(315, 23);
|
||||
this.AclAddrTextBox.TabIndex = 19;
|
||||
this.AclAddrTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// AclLabel
|
||||
//
|
||||
@@ -372,83 +384,75 @@
|
||||
this.AclLabel.TabIndex = 20;
|
||||
this.AclLabel.Text = "Custom ACL";
|
||||
//
|
||||
// DetectionInterval_Label
|
||||
// DetectionIntervalLabel
|
||||
//
|
||||
this.DetectionInterval_Label.AutoSize = true;
|
||||
this.DetectionInterval_Label.Location = new System.Drawing.Point(229, 192);
|
||||
this.DetectionInterval_Label.Name = "DetectionInterval_Label";
|
||||
this.DetectionInterval_Label.Size = new System.Drawing.Size(136, 17);
|
||||
this.DetectionInterval_Label.TabIndex = 18;
|
||||
this.DetectionInterval_Label.Text = "Detection interval(sec)";
|
||||
this.DetectionIntervalLabel.AutoSize = true;
|
||||
this.DetectionIntervalLabel.Location = new System.Drawing.Point(228, 215);
|
||||
this.DetectionIntervalLabel.Name = "DetectionIntervalLabel";
|
||||
this.DetectionIntervalLabel.Size = new System.Drawing.Size(136, 17);
|
||||
this.DetectionIntervalLabel.TabIndex = 18;
|
||||
this.DetectionIntervalLabel.Text = "Detection interval(sec)";
|
||||
//
|
||||
// DetectionInterval_TextBox
|
||||
// DetectionIntervalTextBox
|
||||
//
|
||||
this.DetectionInterval_TextBox.Location = new System.Drawing.Point(367, 189);
|
||||
this.DetectionInterval_TextBox.Name = "DetectionInterval_TextBox";
|
||||
this.DetectionInterval_TextBox.Size = new System.Drawing.Size(68, 23);
|
||||
this.DetectionInterval_TextBox.TabIndex = 17;
|
||||
this.DetectionInterval_TextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.DetectionIntervalTextBox.Location = new System.Drawing.Point(366, 212);
|
||||
this.DetectionIntervalTextBox.Name = "DetectionIntervalTextBox";
|
||||
this.DetectionIntervalTextBox.Size = new System.Drawing.Size(68, 23);
|
||||
this.DetectionIntervalTextBox.TabIndex = 17;
|
||||
this.DetectionIntervalTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// EnableStartedTcping_CheckBox
|
||||
// TcpingAtStartedCheckBox
|
||||
//
|
||||
this.EnableStartedTcping_CheckBox.AutoSize = true;
|
||||
this.EnableStartedTcping_CheckBox.Location = new System.Drawing.Point(144, 191);
|
||||
this.EnableStartedTcping_CheckBox.Name = "EnableStartedTcping_CheckBox";
|
||||
this.EnableStartedTcping_CheckBox.Size = new System.Drawing.Size(66, 21);
|
||||
this.EnableStartedTcping_CheckBox.TabIndex = 15;
|
||||
this.EnableStartedTcping_CheckBox.Text = "Enable";
|
||||
this.EnableStartedTcping_CheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// DelayTestAfterStartup_Label
|
||||
//
|
||||
this.DelayTestAfterStartup_Label.AutoSize = true;
|
||||
this.DelayTestAfterStartup_Label.Location = new System.Drawing.Point(12, 192);
|
||||
this.DelayTestAfterStartup_Label.Name = "DelayTestAfterStartup_Label";
|
||||
this.DelayTestAfterStartup_Label.Size = new System.Drawing.Size(126, 17);
|
||||
this.DelayTestAfterStartup_Label.TabIndex = 16;
|
||||
this.DelayTestAfterStartup_Label.Text = "Delay test after start";
|
||||
this.TcpingAtStartedCheckBox.AutoSize = true;
|
||||
this.TcpingAtStartedCheckBox.Location = new System.Drawing.Point(15, 214);
|
||||
this.TcpingAtStartedCheckBox.Name = "TcpingAtStartedCheckBox";
|
||||
this.TcpingAtStartedCheckBox.Size = new System.Drawing.Size(145, 21);
|
||||
this.TcpingAtStartedCheckBox.TabIndex = 15;
|
||||
this.TcpingAtStartedCheckBox.TabStop = false;
|
||||
this.TcpingAtStartedCheckBox.Text = "Delay test after start";
|
||||
this.TcpingAtStartedCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// STUN_ServerPortTextBox
|
||||
//
|
||||
this.STUN_ServerPortTextBox.Location = new System.Drawing.Point(120, 244);
|
||||
this.STUN_ServerPortTextBox.Location = new System.Drawing.Point(366, 241);
|
||||
this.STUN_ServerPortTextBox.Name = "STUN_ServerPortTextBox";
|
||||
this.STUN_ServerPortTextBox.Size = new System.Drawing.Size(315, 23);
|
||||
this.STUN_ServerPortTextBox.Size = new System.Drawing.Size(68, 23);
|
||||
this.STUN_ServerPortTextBox.TabIndex = 8;
|
||||
this.STUN_ServerPortTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// STUNServerPortLabel
|
||||
// StunTextBoxSplitLabel
|
||||
//
|
||||
this.STUNServerPortLabel.AutoSize = true;
|
||||
this.STUNServerPortLabel.Location = new System.Drawing.Point(12, 247);
|
||||
this.STUNServerPortLabel.Name = "STUNServerPortLabel";
|
||||
this.STUNServerPortLabel.Size = new System.Drawing.Size(110, 17);
|
||||
this.STUNServerPortLabel.TabIndex = 12;
|
||||
this.STUNServerPortLabel.Text = "STUN Server Port";
|
||||
this.StunTextBoxSplitLabel.AutoSize = true;
|
||||
this.StunTextBoxSplitLabel.Location = new System.Drawing.Point(353, 244);
|
||||
this.StunTextBoxSplitLabel.Name = "StunTextBoxSplitLabel";
|
||||
this.StunTextBoxSplitLabel.Size = new System.Drawing.Size(11, 17);
|
||||
this.StunTextBoxSplitLabel.TabIndex = 12;
|
||||
this.StunTextBoxSplitLabel.Text = ":";
|
||||
//
|
||||
// STUNServerLabel
|
||||
//
|
||||
this.STUNServerLabel.AutoSize = true;
|
||||
this.STUNServerLabel.Location = new System.Drawing.Point(12, 221);
|
||||
this.STUNServerLabel.Location = new System.Drawing.Point(12, 244);
|
||||
this.STUNServerLabel.Name = "STUNServerLabel";
|
||||
this.STUNServerLabel.Size = new System.Drawing.Size(82, 17);
|
||||
this.STUNServerLabel.TabIndex = 10;
|
||||
this.STUNServerLabel.Text = "STUN Server";
|
||||
//
|
||||
// RunAtStartup
|
||||
// RunAtStartupCheckBox
|
||||
//
|
||||
this.RunAtStartup.AutoSize = true;
|
||||
this.RunAtStartup.Location = new System.Drawing.Point(12, 75);
|
||||
this.RunAtStartup.Name = "RunAtStartup";
|
||||
this.RunAtStartup.Size = new System.Drawing.Size(109, 21);
|
||||
this.RunAtStartup.TabIndex = 11;
|
||||
this.RunAtStartup.Text = "Run at startup";
|
||||
this.RunAtStartup.UseVisualStyleBackColor = true;
|
||||
this.RunAtStartupCheckBox.AutoSize = true;
|
||||
this.RunAtStartupCheckBox.Location = new System.Drawing.Point(12, 75);
|
||||
this.RunAtStartupCheckBox.Name = "RunAtStartupCheckBox";
|
||||
this.RunAtStartupCheckBox.Size = new System.Drawing.Size(109, 21);
|
||||
this.RunAtStartupCheckBox.TabIndex = 11;
|
||||
this.RunAtStartupCheckBox.Text = "Run at startup";
|
||||
this.RunAtStartupCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// STUN_ServerTextBox
|
||||
//
|
||||
this.STUN_ServerTextBox.Location = new System.Drawing.Point(120, 215);
|
||||
this.STUN_ServerTextBox.Location = new System.Drawing.Point(120, 241);
|
||||
this.STUN_ServerTextBox.Name = "STUN_ServerTextBox";
|
||||
this.STUN_ServerTextBox.Size = new System.Drawing.Size(315, 23);
|
||||
this.STUN_ServerTextBox.Size = new System.Drawing.Size(233, 23);
|
||||
this.STUN_ServerTextBox.TabIndex = 11;
|
||||
this.STUN_ServerTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
@@ -462,22 +466,33 @@
|
||||
this.MinimizeWhenStartedCheckBox.Text = "Minimize when started";
|
||||
this.MinimizeWhenStartedCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// ProfileCount_Label
|
||||
// ProfileCountLabel
|
||||
//
|
||||
this.ProfileCount_Label.AutoSize = true;
|
||||
this.ProfileCount_Label.Location = new System.Drawing.Point(12, 167);
|
||||
this.ProfileCount_Label.Name = "ProfileCount_Label";
|
||||
this.ProfileCount_Label.Size = new System.Drawing.Size(79, 17);
|
||||
this.ProfileCount_Label.TabIndex = 8;
|
||||
this.ProfileCount_Label.Text = "ProfileCount";
|
||||
this.ProfileCountLabel.AutoSize = true;
|
||||
this.ProfileCountLabel.Location = new System.Drawing.Point(12, 188);
|
||||
this.ProfileCountLabel.Name = "ProfileCountLabel";
|
||||
this.ProfileCountLabel.Size = new System.Drawing.Size(79, 17);
|
||||
this.ProfileCountLabel.TabIndex = 8;
|
||||
this.ProfileCountLabel.Text = "ProfileCount";
|
||||
//
|
||||
// ProfileCount_TextBox
|
||||
// ProfileCountTextBox
|
||||
//
|
||||
this.ProfileCount_TextBox.Location = new System.Drawing.Point(120, 164);
|
||||
this.ProfileCount_TextBox.Name = "ProfileCount_TextBox";
|
||||
this.ProfileCount_TextBox.Size = new System.Drawing.Size(90, 23);
|
||||
this.ProfileCount_TextBox.TabIndex = 9;
|
||||
this.ProfileCount_TextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.ProfileCountTextBox.Location = new System.Drawing.Point(120, 185);
|
||||
this.ProfileCountTextBox.Name = "ProfileCountTextBox";
|
||||
this.ProfileCountTextBox.Size = new System.Drawing.Size(90, 23);
|
||||
this.ProfileCountTextBox.TabIndex = 9;
|
||||
this.ProfileCountTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// CheckBetaUpdateCheckBox
|
||||
//
|
||||
this.CheckBetaUpdateCheckBox.AutoSize = true;
|
||||
this.CheckBetaUpdateCheckBox.Location = new System.Drawing.Point(206, 102);
|
||||
this.CheckBetaUpdateCheckBox.Name = "CheckBetaUpdateCheckBox";
|
||||
this.CheckBetaUpdateCheckBox.Size = new System.Drawing.Size(137, 21);
|
||||
this.CheckBetaUpdateCheckBox.TabIndex = 8;
|
||||
this.CheckBetaUpdateCheckBox.Text = "Check Beta update";
|
||||
this.CheckBetaUpdateCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
this.CheckBetaUpdateCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// CheckUpdateWhenOpenedCheckBox
|
||||
//
|
||||
@@ -578,26 +593,27 @@
|
||||
private System.Windows.Forms.CheckBox StopWhenExitedCheckBox;
|
||||
private System.Windows.Forms.CheckBox StartWhenOpenedCheckBox;
|
||||
private System.Windows.Forms.CheckBox CheckUpdateWhenOpenedCheckBox;
|
||||
private System.Windows.Forms.Label ProfileCount_Label;
|
||||
private System.Windows.Forms.TextBox ProfileCount_TextBox;
|
||||
private System.Windows.Forms.Label ProfileCountLabel;
|
||||
private System.Windows.Forms.TextBox ProfileCountTextBox;
|
||||
private System.Windows.Forms.CheckBox MinimizeWhenStartedCheckBox;
|
||||
private System.Windows.Forms.CheckBox RunAtStartup;
|
||||
private System.Windows.Forms.Label STUNServerPortLabel;
|
||||
private System.Windows.Forms.CheckBox RunAtStartupCheckBox;
|
||||
private System.Windows.Forms.Label StunTextBoxSplitLabel;
|
||||
private System.Windows.Forms.Label STUNServerLabel;
|
||||
private System.Windows.Forms.TextBox STUN_ServerTextBox;
|
||||
private System.Windows.Forms.TextBox STUN_ServerPortTextBox;
|
||||
private System.Windows.Forms.CheckBox ProxyDNSCheckBox;
|
||||
private System.Windows.Forms.TextBox DetectionInterval_TextBox;
|
||||
private System.Windows.Forms.CheckBox EnableStartedTcping_CheckBox;
|
||||
private System.Windows.Forms.Label DelayTestAfterStartup_Label;
|
||||
private System.Windows.Forms.Label DetectionInterval_Label;
|
||||
private System.Windows.Forms.TextBox DetectionIntervalTextBox;
|
||||
private System.Windows.Forms.CheckBox TcpingAtStartedCheckBox;
|
||||
private System.Windows.Forms.Label DetectionIntervalLabel;
|
||||
private System.Windows.Forms.Label RedirectorLabel;
|
||||
private System.Windows.Forms.TextBox RedirectorTextBox;
|
||||
private System.Windows.Forms.TextBox AclAddr;
|
||||
private System.Windows.Forms.TextBox AclAddrTextBox;
|
||||
private System.Windows.Forms.Label AclLabel;
|
||||
private System.Windows.Forms.CheckBox UseFakeDNSCheckBox;
|
||||
private System.Windows.Forms.CheckBox BootShadowsocksFromDLLCheckBox;
|
||||
private System.Windows.Forms.Label LanguageLabel;
|
||||
private System.Windows.Forms.ComboBox LanguageComboBox;
|
||||
private System.Windows.Forms.CheckBox ModifySystemDNSCheckBox;
|
||||
private System.Windows.Forms.CheckBox CheckBetaUpdateCheckBox;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
using TaskScheduler;
|
||||
|
||||
@@ -33,6 +34,7 @@ namespace Netch.Forms
|
||||
dns += ip;
|
||||
dns += ',';
|
||||
}
|
||||
|
||||
dns = dns.Trim();
|
||||
TUNTAPDNSTextBox.Text = dns.Substring(0, dns.Length - 1);
|
||||
}
|
||||
@@ -44,9 +46,82 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
private void InitValue()
|
||||
{
|
||||
// Local Port
|
||||
Socks5PortTextBox.Text = Global.Settings.Socks5LocalPort.ToString();
|
||||
HTTPPortTextBox.Text = Global.Settings.HTTPLocalPort.ToString();
|
||||
RedirectorTextBox.Text = Global.Settings.RedirectorTCPPort.ToString();
|
||||
switch (Global.Settings.LocalAddress)
|
||||
{
|
||||
case "127.0.0.1":
|
||||
AllowDevicesCheckBox.Checked = false;
|
||||
break;
|
||||
case "0.0.0.0":
|
||||
AllowDevicesCheckBox.Checked = true;
|
||||
break;
|
||||
default:
|
||||
Global.Settings.LocalAddress = "127.0.0.1";
|
||||
AllowDevicesCheckBox.Checked = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// TUN/TAP
|
||||
TUNTAPAddressTextBox.Text = Global.Settings.TUNTAP.Address;
|
||||
TUNTAPNetmaskTextBox.Text = Global.Settings.TUNTAP.Netmask;
|
||||
TUNTAPGatewayTextBox.Text = Global.Settings.TUNTAP.Gateway;
|
||||
UseCustomDNSCheckBox.Checked = Global.Settings.TUNTAP.UseCustomDNS;
|
||||
ProxyDNSCheckBox.Checked = Global.Settings.TUNTAP.ProxyDNS;
|
||||
UseFakeDNSCheckBox.Checked = Global.Settings.TUNTAP.UseFakeDNS;
|
||||
if (Global.Settings.TUNTAP.DNS.Count > 0)
|
||||
{
|
||||
var dns = "";
|
||||
foreach (var ip in Global.Settings.TUNTAP.DNS)
|
||||
{
|
||||
dns += ip;
|
||||
dns += ',';
|
||||
}
|
||||
|
||||
dns = dns.Trim();
|
||||
TUNTAPDNSTextBox.Text = dns.Substring(0, dns.Length - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果 DNS 为空,设置为默认 DNS 1.1.1.1
|
||||
Global.Settings.TUNTAP.DNS.Add("1.1.1.1");
|
||||
TUNTAPDNSTextBox.Text = "1.1.1.1";
|
||||
}
|
||||
|
||||
if (!UseCustomDNSCheckBox.Checked)
|
||||
{
|
||||
TUNTAPDNSTextBox.Enabled = false;
|
||||
}
|
||||
|
||||
// Behavior
|
||||
ExitWhenClosedCheckBox.Checked = Global.Settings.ExitWhenClosed;
|
||||
StopWhenExitedCheckBox.Checked = Global.Settings.StopWhenExited;
|
||||
StartWhenOpenedCheckBox.Checked = Global.Settings.StartWhenOpened;
|
||||
MinimizeWhenStartedCheckBox.Checked = Global.Settings.MinimizeWhenStarted;
|
||||
RunAtStartupCheckBox.Checked = Global.Settings.RunAtStartup;
|
||||
CheckUpdateWhenOpenedCheckBox.Checked = Global.Settings.CheckUpdateWhenOpened;
|
||||
BootShadowsocksFromDLLCheckBox.Checked = Global.Settings.BootShadowsocksFromDLL;
|
||||
ModifySystemDNSCheckBox.Checked = Global.Settings.ModifySystemDNS;
|
||||
CheckBetaUpdateCheckBox.Checked = Global.Settings.CheckBetaUpdate;
|
||||
|
||||
ProfileCountTextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
TcpingAtStartedCheckBox.Checked = Global.Settings.StartedTcping;
|
||||
DetectionIntervalTextBox.Text = Global.Settings.StartedTcping_Interval.ToString();
|
||||
STUN_ServerTextBox.Text = Global.Settings.STUN_Server;
|
||||
STUN_ServerPortTextBox.Text = Global.Settings.STUN_Server_Port.ToString();
|
||||
AclAddrTextBox.Text = Global.Settings.ACL;
|
||||
LanguageComboBox.Items.AddRange(i18N.GetTranslateList().ToArray());
|
||||
LanguageComboBox.SelectedItem = Global.Settings.Language;
|
||||
}
|
||||
|
||||
private void InitText()
|
||||
{
|
||||
Text = i18N.Translate(Text);
|
||||
|
||||
PortGroupBox.Text = i18N.Translate(PortGroupBox.Text);
|
||||
AllowDevicesCheckBox.Text = i18N.Translate(AllowDevicesCheckBox.Text);
|
||||
TUNTAPAddressLabel.Text = i18N.Translate(TUNTAPAddressLabel.Text);
|
||||
@@ -58,97 +133,29 @@ namespace Netch.Forms
|
||||
GlobalBypassIPsButton.Text = i18N.Translate(GlobalBypassIPsButton.Text);
|
||||
ControlButton.Text = i18N.Translate(ControlButton.Text);
|
||||
BootShadowsocksFromDLLCheckBox.Text = i18N.Translate(BootShadowsocksFromDLLCheckBox.Text);
|
||||
|
||||
ExitWhenClosedCheckBox.Checked = Global.Settings.ExitWhenClosed;
|
||||
StopWhenExitedCheckBox.Checked = Global.Settings.StopWhenExited;
|
||||
StartWhenOpenedCheckBox.Checked = Global.Settings.StartWhenOpened;
|
||||
CheckUpdateWhenOpenedCheckBox.Checked = Global.Settings.CheckUpdateWhenOpened;
|
||||
MinimizeWhenStartedCheckBox.Checked = Global.Settings.MinimizeWhenStarted;
|
||||
RunAtStartup.Checked = Global.Settings.RunAtStartup;
|
||||
EnableStartedTcping_CheckBox.Checked = Global.Settings.StartedTcping;
|
||||
DetectionInterval_TextBox.Text = Global.Settings.StartedTcping_Interval.ToString();
|
||||
BootShadowsocksFromDLLCheckBox.Checked = Global.Settings.BootShadowsocksFromDLL;
|
||||
|
||||
Socks5PortTextBox.Text = Global.Settings.Socks5LocalPort.ToString();
|
||||
HTTPPortTextBox.Text = Global.Settings.HTTPLocalPort.ToString();
|
||||
RedirectorTextBox.Text = Global.Settings.RedirectorTCPPort.ToString();
|
||||
|
||||
TUNTAPAddressTextBox.Text = Global.Settings.TUNTAP.Address;
|
||||
TUNTAPNetmaskTextBox.Text = Global.Settings.TUNTAP.Netmask;
|
||||
TUNTAPGatewayTextBox.Text = Global.Settings.TUNTAP.Gateway;
|
||||
|
||||
UseCustomDNSCheckBox.Checked = Global.Settings.TUNTAP.UseCustomDNS;
|
||||
ProxyDNSCheckBox.Checked = Global.Settings.TUNTAP.ProxyDNS;
|
||||
UseFakeDNSCheckBox.Checked = Global.Settings.TUNTAP.UseFakeDNS;
|
||||
|
||||
ModifySystemDNSCheckBox.Text = i18N.Translate(ModifySystemDNSCheckBox.Text);
|
||||
CheckBetaUpdateCheckBox.Text = i18N.Translate(CheckBetaUpdateCheckBox.Text);
|
||||
BehaviorGroupBox.Text = i18N.Translate(BehaviorGroupBox.Text);
|
||||
ExitWhenClosedCheckBox.Text = i18N.Translate(ExitWhenClosedCheckBox.Text);
|
||||
StopWhenExitedCheckBox.Text = i18N.Translate(StopWhenExitedCheckBox.Text);
|
||||
StartWhenOpenedCheckBox.Text = i18N.Translate(StartWhenOpenedCheckBox.Text);
|
||||
MinimizeWhenStartedCheckBox.Text = i18N.Translate(MinimizeWhenStartedCheckBox.Text);
|
||||
RunAtStartup.Text = i18N.Translate(RunAtStartup.Text);
|
||||
RunAtStartupCheckBox.Text = i18N.Translate(RunAtStartupCheckBox.Text);
|
||||
CheckUpdateWhenOpenedCheckBox.Text = i18N.Translate(CheckUpdateWhenOpenedCheckBox.Text);
|
||||
ProfileCount_Label.Text = i18N.Translate(ProfileCount_Label.Text);
|
||||
DelayTestAfterStartup_Label.Text = i18N.Translate(DelayTestAfterStartup_Label.Text);
|
||||
EnableStartedTcping_CheckBox.Text = i18N.Translate(EnableStartedTcping_CheckBox.Text);
|
||||
DetectionInterval_Label.Text = i18N.Translate(DetectionInterval_Label.Text);
|
||||
DelayTestAfterStartup_Label.Text = i18N.Translate(DelayTestAfterStartup_Label.Text);
|
||||
ProfileCountLabel.Text = i18N.Translate(ProfileCountLabel.Text);
|
||||
TcpingAtStartedCheckBox.Text = i18N.Translate(TcpingAtStartedCheckBox.Text);
|
||||
DetectionIntervalLabel.Text = i18N.Translate(DetectionIntervalLabel.Text);
|
||||
STUNServerLabel.Text = i18N.Translate(STUNServerLabel.Text);
|
||||
STUNServerPortLabel.Text = i18N.Translate(STUNServerPortLabel.Text);
|
||||
|
||||
ProfileCount_TextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
STUN_ServerTextBox.Text = Global.Settings.STUN_Server;
|
||||
STUN_ServerPortTextBox.Text = Global.Settings.STUN_Server_Port.ToString();
|
||||
|
||||
StunTextBoxSplitLabel.Text = i18N.Translate(StunTextBoxSplitLabel.Text);
|
||||
AclLabel.Text = i18N.Translate(AclLabel.Text);
|
||||
AclAddr.Text = Global.Settings.ACL;
|
||||
|
||||
LanguageLabel.Text = i18N.Translate(LanguageLabel.Text);
|
||||
LanguageComboBox.Items.AddRange(i18N.GetTranslateList().ToArray());
|
||||
LanguageComboBox.SelectedItem = Global.Settings.Language;
|
||||
}
|
||||
|
||||
private void SettingForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
InitText();
|
||||
|
||||
if (Global.Settings.TUNTAP.DNS.Count > 0)
|
||||
{
|
||||
var dns = "";
|
||||
foreach (var ip in Global.Settings.TUNTAP.DNS)
|
||||
{
|
||||
dns += ip;
|
||||
dns += ',';
|
||||
}
|
||||
dns = dns.Trim();
|
||||
TUNTAPDNSTextBox.Text = dns.Substring(0, dns.Length - 1);
|
||||
}
|
||||
// 如果 DNS 为空,设置为默认 DNS 1.1.1.1
|
||||
else
|
||||
{
|
||||
Global.Settings.TUNTAP.DNS.Add("1.1.1.1");
|
||||
TUNTAPDNSTextBox.Text = "1.1.1.1";
|
||||
}
|
||||
|
||||
if (!UseCustomDNSCheckBox.Checked)
|
||||
{
|
||||
TUNTAPDNSTextBox.Enabled = false;
|
||||
}
|
||||
|
||||
// 设置本地代理是否允许其他设备连接
|
||||
if (Global.Settings.LocalAddress == "127.0.0.1")
|
||||
{
|
||||
AllowDevicesCheckBox.Checked = false;
|
||||
}
|
||||
else if (Global.Settings.LocalAddress == "0.0.0.0")
|
||||
{
|
||||
AllowDevicesCheckBox.Checked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.Settings.LocalAddress = "127.0.0.1";
|
||||
AllowDevicesCheckBox.Checked = false;
|
||||
}
|
||||
InitValue();
|
||||
}
|
||||
|
||||
private void SettingForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
@@ -168,8 +175,9 @@ namespace Netch.Forms
|
||||
Global.Settings.StopWhenExited = StopWhenExitedCheckBox.Checked;
|
||||
Global.Settings.StartWhenOpened = StartWhenOpenedCheckBox.Checked;
|
||||
Global.Settings.CheckUpdateWhenOpened = CheckUpdateWhenOpenedCheckBox.Checked;
|
||||
Global.Settings.CheckBetaUpdate = CheckBetaUpdateCheckBox.Checked;
|
||||
Global.Settings.MinimizeWhenStarted = MinimizeWhenStartedCheckBox.Checked;
|
||||
Global.Settings.RunAtStartup = RunAtStartup.Checked;
|
||||
Global.Settings.RunAtStartup = RunAtStartupCheckBox.Checked;
|
||||
Global.Settings.BootShadowsocksFromDLL = BootShadowsocksFromDLLCheckBox.Checked;
|
||||
Global.Settings.Language = LanguageComboBox.SelectedItem.ToString();
|
||||
|
||||
@@ -183,9 +191,12 @@ namespace Netch.Forms
|
||||
folder.GetTask("Netch Startup");
|
||||
taskIsExists = true;
|
||||
}
|
||||
catch (Exception) { }
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (RunAtStartup.Checked)
|
||||
if (RunAtStartupCheckBox.Checked)
|
||||
{
|
||||
if (taskIsExists)
|
||||
folder.DeleteTask("Netch Startup", 0);
|
||||
@@ -196,7 +207,7 @@ namespace Netch.Forms
|
||||
task.Principal.RunLevel = _TASK_RUNLEVEL.TASK_RUNLEVEL_HIGHEST;
|
||||
|
||||
task.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_LOGON);
|
||||
var action = (IExecAction)task.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC);
|
||||
var action = (IExecAction) task.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC);
|
||||
action.Path = Application.ExecutablePath;
|
||||
|
||||
|
||||
@@ -204,7 +215,7 @@ namespace Netch.Forms
|
||||
task.Settings.DisallowStartIfOnBatteries = false;
|
||||
task.Settings.RunOnlyIfIdle = false;
|
||||
|
||||
folder.RegisterTaskDefinition("Netch Startup", task, (int)_TASK_CREATION.TASK_CREATE, null, null, _TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN, "");
|
||||
folder.RegisterTaskDefinition("Netch Startup", task, (int) _TASK_CREATION.TASK_CREATE, null, null, _TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -212,77 +223,15 @@ namespace Netch.Forms
|
||||
folder.DeleteTask("Netch Startup", 0);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var Socks5Port = int.Parse(Socks5PortTextBox.Text);
|
||||
|
||||
if (Socks5Port > 0 && Socks5Port < 65536)
|
||||
{
|
||||
Global.Settings.Socks5LocalPort = Socks5Port;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
Socks5PortTextBox.Text = Global.Settings.Socks5LocalPort.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("Port value illegal. Try again."));
|
||||
|
||||
// 端口检查
|
||||
if (!CheckPortText("Socks5", ref Socks5PortTextBox, ref Global.Settings.Socks5LocalPort))
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var HTTPPort = int.Parse(HTTPPortTextBox.Text);
|
||||
|
||||
if (HTTPPort > 0 && HTTPPort < 65536)
|
||||
{
|
||||
Global.Settings.HTTPLocalPort = HTTPPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
HTTPPortTextBox.Text = Global.Settings.HTTPLocalPort.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("Port value illegal. Try again."));
|
||||
|
||||
if (!CheckPortText("HTTP", ref HTTPPortTextBox, ref Global.Settings.HTTPLocalPort))
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var RedirectorPort = int.Parse(RedirectorTextBox.Text);
|
||||
|
||||
if (RedirectorPort > 0 && RedirectorPort < 65536)
|
||||
{
|
||||
Global.Settings.RedirectorTCPPort = RedirectorPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
RedirectorTextBox.Text = Global.Settings.RedirectorTCPPort.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("Port value illegal. Try again."));
|
||||
|
||||
if (!CheckPortText("RedirectorTCP", ref RedirectorTextBox, ref Global.Settings.RedirectorTCPPort, PortType.TCP))
|
||||
return;
|
||||
}
|
||||
|
||||
if (AllowDevicesCheckBox.Checked)
|
||||
{
|
||||
Global.Settings.LocalAddress = "0.0.0.0";
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.Settings.LocalAddress = "127.0.0.1";
|
||||
}
|
||||
Global.Settings.LocalAddress = AllowDevicesCheckBox.Checked ? "0.0.0.0" : "127.0.0.1";
|
||||
|
||||
try
|
||||
{
|
||||
@@ -310,15 +259,17 @@ namespace Netch.Forms
|
||||
DNS += ip;
|
||||
DNS += ',';
|
||||
}
|
||||
|
||||
DNS = DNS.Trim();
|
||||
TUNTAPDNSTextBox.Text = DNS.Substring(0, DNS.Length - 1);
|
||||
UseCustomDNSCheckBox.Checked = Global.Settings.TUNTAP.UseCustomDNS;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var ProfileCount = int.Parse(ProfileCount_TextBox.Text);
|
||||
var ProfileCount = int.Parse(ProfileCountTextBox.Text);
|
||||
|
||||
if (ProfileCount > -1)
|
||||
{
|
||||
@@ -331,11 +282,12 @@ namespace Netch.Forms
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
ProfileCount_TextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
ProfileCountTextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("ProfileCount value illegal. Try again."));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var STUN_Server = STUN_ServerTextBox.Text;
|
||||
@@ -354,16 +306,17 @@ namespace Netch.Forms
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
ProfileCount_TextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
ProfileCountTextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("STUN_ServerPort value illegal. Try again."));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Global.Settings.StartedTcping = EnableStartedTcping_CheckBox.Checked;
|
||||
Global.Settings.StartedTcping = TcpingAtStartedCheckBox.Checked;
|
||||
|
||||
var DetectionInterval = int.Parse(DetectionInterval_TextBox.Text);
|
||||
var DetectionInterval = int.Parse(DetectionIntervalTextBox.Text);
|
||||
|
||||
if (DetectionInterval > 0)
|
||||
{
|
||||
@@ -376,13 +329,13 @@ namespace Netch.Forms
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
ProfileCount_TextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
ProfileCountTextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
MessageBoxX.Show(i18N.Translate("Detection interval value illegal. Try again."));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Global.Settings.ACL = AclAddr.Text;
|
||||
Global.Settings.ACL = AclAddrTextBox.Text;
|
||||
|
||||
Global.Settings.TUNTAP.Address = TUNTAPAddressTextBox.Text;
|
||||
Global.Settings.TUNTAP.Netmask = TUNTAPNetmaskTextBox.Text;
|
||||
@@ -398,9 +351,53 @@ namespace Netch.Forms
|
||||
Global.Settings.TUNTAP.ProxyDNS = ProxyDNSCheckBox.Checked;
|
||||
Global.Settings.TUNTAP.UseFakeDNS = UseFakeDNSCheckBox.Checked;
|
||||
|
||||
Global.Settings.ModifySystemDNS = ModifySystemDNSCheckBox.Checked;
|
||||
|
||||
Configuration.Save();
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="portName"></param>
|
||||
/// <param name="portTextBox"></param>
|
||||
/// <param name="originPort"></param>
|
||||
/// <param name="portType"></param>
|
||||
/// <returns></returns>
|
||||
private bool CheckPortText(string portName, ref TextBox portTextBox, ref int originPort, PortType portType = PortType.Both)
|
||||
{
|
||||
// 端口检查
|
||||
try
|
||||
{
|
||||
var port = int.Parse(portTextBox.Text);
|
||||
|
||||
if (port <= 0 || port >= 65536)
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
if (port == originPort)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PortHelper.PortInUse(port, portType))
|
||||
{
|
||||
MessageBoxX.Show(i18N.TranslateFormat("The {0} port is in use.", portName));
|
||||
return false;
|
||||
}
|
||||
|
||||
originPort = port;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Port value illegal. Try again."));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ namespace Netch.Forms
|
||||
|
||||
Configuration.Save();
|
||||
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
|
||||
MessageBoxX.Show(i18N.Translate("Successfully saved"));
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
}
|
||||
});
|
||||
if (saveFlag)
|
||||
@@ -200,7 +200,7 @@ namespace Netch.Forms
|
||||
{
|
||||
Configuration.Save();
|
||||
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
|
||||
MessageBoxX.Show(i18N.Translate("Successfully saved"));
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Close();
|
||||
}
|
||||
/// <summary>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Netch
|
||||
{
|
||||
@@ -11,12 +12,12 @@ namespace Netch
|
||||
/// <summary>
|
||||
/// 换行
|
||||
/// </summary>
|
||||
public static string EOF = "\r\n";
|
||||
|
||||
public static readonly string NetchDir = (AppDomain.CurrentDomain.BaseDirectory).TrimEnd();
|
||||
public const string EOF = "\r\n";
|
||||
|
||||
public static readonly string NetchDir = Application.StartupPath;
|
||||
|
||||
/// <summary>
|
||||
/// 主窗体
|
||||
/// 主窗体的静态实例
|
||||
/// </summary>
|
||||
public static Forms.MainForm MainForm;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Netch.Models.GitHubRelease
|
||||
{
|
||||
releases = releases.Where(release => !release.prerelease);
|
||||
}
|
||||
|
||||
releases = releases.Where(release => IsVersionString(release.tag_name));
|
||||
var ordered = releases.OrderByDescending(release => release.tag_name, new VersionComparer());
|
||||
return ordered.ElementAt(0);
|
||||
@@ -19,6 +20,8 @@ namespace Netch.Models.GitHubRelease
|
||||
|
||||
private static bool IsVersionString(string str)
|
||||
{
|
||||
if (Global.Settings.CheckBetaUpdate)
|
||||
str = str.Split('-')[0];
|
||||
return Version.TryParse(str, out _);
|
||||
}
|
||||
|
||||
@@ -27,10 +30,28 @@ namespace Netch.Models.GitHubRelease
|
||||
/// <returns> <0:version2 is greater</returns>
|
||||
public static int CompareVersion(string v1, string v2)
|
||||
{
|
||||
var version1 = new Version(v1);
|
||||
var version2 = new Version(v2);
|
||||
var version1 = ToVersion(v1);
|
||||
var version2 = ToVersion(v2);
|
||||
var res = version1.CompareTo(version2);
|
||||
return res;
|
||||
}
|
||||
|
||||
private static Version ToVersion(string versionStr)
|
||||
{
|
||||
var v = versionStr.Split('-');
|
||||
var version = Version.Parse(v[0]);
|
||||
if (v.Length == 1)
|
||||
return version;
|
||||
var beta = v[1];
|
||||
|
||||
var result = string.Empty;
|
||||
foreach (var c in beta)
|
||||
{
|
||||
if (int.TryParse(c.ToString(), out var n))
|
||||
result += n;
|
||||
}
|
||||
|
||||
return new Version(version.Major, version.Minor, version.Build, int.Parse(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Netch/Models/LogLevel.cs
Normal file
9
Netch/Models/LogLevel.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Netch.Models
|
||||
{
|
||||
public enum LogLevel
|
||||
{
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,13 @@ namespace Netch.Models
|
||||
public string FileName;
|
||||
|
||||
/// <summary>
|
||||
/// 类型
|
||||
/// 0. 进程加速
|
||||
/// 1. TUN/TAP 规则内 IP CIDR 加速
|
||||
/// 2. TUN/TAP 全局,绕过规则内 IP CIDR
|
||||
/// 3. HTTP 代理(自动设置到系统代理)
|
||||
/// 4. Socks5 代理(不自动设置到系统代理)
|
||||
/// 5. Socks5 + HTTP 代理(不自动设置到系统代理)
|
||||
/// 类型<para />
|
||||
/// 0. Socks5 + 进程加速<para />
|
||||
/// 1. Socks5 + TUN/TAP 规则内 IP CIDR 加速<para />
|
||||
/// 2. Socks5 + TUN/TAP 全局,绕过规则内 IP CIDR<para />
|
||||
/// 3. Socks5 + HTTP 代理(自动设置到系统代理)<para />
|
||||
/// 4. Socks5 代理(不自动设置到系统代理)<para />
|
||||
/// 5. Socks5 + HTTP 代理(不自动设置到系统代理)<para />
|
||||
/// </summary>
|
||||
public int Type = 0;
|
||||
|
||||
|
||||
@@ -87,6 +87,21 @@ namespace Netch.Models
|
||||
/// 是否打开软件时检查更新
|
||||
/// </summary>
|
||||
public bool CheckUpdateWhenOpened = true;
|
||||
|
||||
/// <summary>
|
||||
/// 是否检查 Beta 更新
|
||||
/// </summary>
|
||||
public bool CheckBetaUpdate = false;
|
||||
|
||||
/// <summary>
|
||||
/// 修改系统 DNS
|
||||
/// </summary>
|
||||
public bool ModifySystemDNS = false;
|
||||
|
||||
/// <summary>
|
||||
/// 网页请求超时 毫秒
|
||||
/// </summary>
|
||||
public int RequestTimeout = 10000;
|
||||
|
||||
/// <summary>
|
||||
/// 使用何种模式文件名
|
||||
@@ -109,6 +124,11 @@ namespace Netch.Models
|
||||
/// </summary>
|
||||
public int RedirectorTCPPort = 3901;
|
||||
|
||||
/// <summary>
|
||||
/// UDP Socket 占用端口
|
||||
/// </summary>
|
||||
public int UDPSocketPort = 18291;
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 和 Socks5 本地代理地址
|
||||
/// </summary>
|
||||
|
||||
@@ -8,37 +8,25 @@ namespace Netch
|
||||
/// 创建路由规则
|
||||
/// </summary>
|
||||
/// <param name="address">目标地址</param>
|
||||
/// <param name="netmask">掩码地址</param>
|
||||
/// <param name="cidr">CIDR</param>
|
||||
/// <param name="gateway">网关地址</param>
|
||||
/// <param name="index">适配器索引</param>
|
||||
/// <param name="metric">跃点数</param>
|
||||
/// <returns>是否成功</returns>
|
||||
[DllImport("bin\\NetchCore", CallingConvention = CallingConvention.Cdecl, EntryPoint = "CreateRoute")]
|
||||
public static extern bool CreateRoute(string address, int netmask, string gateway, int index, int metric = 0);
|
||||
|
||||
/// <summary>
|
||||
/// 修改路由规则
|
||||
/// </summary>
|
||||
/// <param name="address">目标地址</param>
|
||||
/// <param name="netmask">掩码地址</param>
|
||||
/// <param name="gateway">网关地址</param>
|
||||
/// <param name="index">适配器索引</param>
|
||||
/// <param name="metric">跃点数</param>
|
||||
/// <returns>是否成功</returns>
|
||||
[DllImport("bin\\NetchCore", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ChangeRoute")]
|
||||
public static extern bool ChangeRoute(string address, int netmask, string gateway, int index, int metric = 0);
|
||||
public static extern bool CreateRoute(string address, int cidr, string gateway, int index, int metric = 0);
|
||||
|
||||
/// <summary>
|
||||
/// 删除路由规则
|
||||
/// </summary>
|
||||
/// <param name="address">目标地址</param>
|
||||
/// <param name="netmask">掩码地址</param>
|
||||
/// <param name="cidr">掩码地址</param>
|
||||
/// <param name="gateway">网关地址</param>
|
||||
/// <param name="index">适配器索引</param>
|
||||
/// <param name="metric">跃点数</param>
|
||||
/// <returns>是否成功</returns>
|
||||
[DllImport("bin\\NetchCore", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DeleteRoute")]
|
||||
public static extern bool DeleteRoute(string address, int netmask, string gateway, int index, int metric = 0);
|
||||
public static extern bool DeleteRoute(string address, int cidr, string gateway, int index, int metric = 0);
|
||||
|
||||
/// <summary>
|
||||
/// 设置直连
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -19,7 +21,7 @@ namespace Netch
|
||||
using (var mutex = new Mutex(false, "Global\\Netch"))
|
||||
{
|
||||
// 设置当前目录
|
||||
Directory.SetCurrentDirectory(Application.StartupPath);
|
||||
Directory.SetCurrentDirectory(Global.NetchDir);
|
||||
|
||||
// 清理上一次的日志文件,防止淤积占用磁盘空间
|
||||
if (Directory.Exists("logging"))
|
||||
@@ -38,7 +40,7 @@ namespace Netch
|
||||
}
|
||||
|
||||
// 预创建目录
|
||||
var directories = new[] { "mode", "data", "i18n", "logging" };
|
||||
var directories = new[] {"mode", "data", "i18n", "logging"};
|
||||
foreach (var item in directories)
|
||||
{
|
||||
// 检查是否已经存在
|
||||
@@ -55,32 +57,22 @@ namespace Netch
|
||||
// 加载语言
|
||||
i18N.Load(Global.Settings.Language);
|
||||
|
||||
// 记录当前系统语言
|
||||
Logging.Info($"当前语言:{Global.Settings.Language}");
|
||||
Task.Run(() =>
|
||||
{
|
||||
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
|
||||
Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Application.ExecutablePath)}");
|
||||
});
|
||||
|
||||
// 检查是否已经运行
|
||||
if (!mutex.WaitOne(0, false))
|
||||
{
|
||||
// 弹出提示
|
||||
MessageBoxX.Show(i18N.Translate("Netch is already running"));
|
||||
OnlyInstance.Send(OnlyInstance.Commands.Show);
|
||||
|
||||
// 退出进程
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
var OS = Environment.Is64BitOperatingSystem ? "x64" : "x86";
|
||||
var PROC = Environment.Is64BitProcess ? "x64" : "x86";
|
||||
|
||||
// 如果系统位数与程序位数不一致
|
||||
if (OS != PROC)
|
||||
{
|
||||
|
||||
// 弹出提示
|
||||
MessageBoxX.Show($"{i18N.Translate("Netch is not compatible with your system.")}\n{i18N.Translate("Current arch of Netch:")} {PROC}\n{i18N.Translate("Current arch of system:")} {OS}");
|
||||
|
||||
// 退出进程
|
||||
Environment.Exit(1);
|
||||
}
|
||||
Task.Run(OnlyInstance.Server);
|
||||
|
||||
// 绑定错误捕获
|
||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
@@ -94,11 +86,8 @@ namespace Netch
|
||||
|
||||
public static void Application_OnException(object sender, ThreadExceptionEventArgs e)
|
||||
{
|
||||
if (!e.Exception.ToString().Contains("ComboBox"))
|
||||
{
|
||||
MessageBox.Show(e.Exception.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
//Application.Exit();
|
||||
Logging.Error(e.Exception.ToString());
|
||||
Utils.Utils.Open(Logging.LogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ini-parser" Version="2.5.2" />
|
||||
<PackageReference Include="IPNetwork2" Version="2.5.211" />
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="3.2.0" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.56" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
@@ -70,6 +71,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Web" />
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace Netch.Override
|
||||
{
|
||||
public class WebClient : System.Net.WebClient
|
||||
{
|
||||
protected override WebRequest GetWebRequest(Uri address)
|
||||
{
|
||||
var request = base.GetWebRequest(address);
|
||||
request.Timeout = 10000;
|
||||
((HttpWebRequest)request).ReadWriteTimeout = 10000;
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,9 @@
|
||||
"Information": "信息",
|
||||
"Error": "错误",
|
||||
|
||||
"If this is your first time using this software,\n please check https://netch.org to install supports first,\n or the program may report errors.": "如果你是第一次使用本软件,\n请务必前往 https://netch.org 安装程序所需依赖,\n否则程序将无法正常运行!",
|
||||
"Netch is already running": "Netch 已经在运行中",
|
||||
"Netch is not compatible with your system.": "Netch 和你的系统不兼容",
|
||||
"Current arch of Netch:": "当前 Netch 架构:",
|
||||
"Current arch of system:": "当前系统架构:",
|
||||
"Missing File or runtime components": "缺少文件或运行库",
|
||||
|
||||
"Start": "启动",
|
||||
"Stop": "停止",
|
||||
@@ -17,7 +16,6 @@
|
||||
"Stopping": "正在停止中",
|
||||
"Stopped": "已停止",
|
||||
"Starting ": "正在启动 ",
|
||||
"v2ray": "V2Ray",
|
||||
"Starting Tap": "正在启动 TUN/TAP",
|
||||
"Starting NatTester": "正在启动 NAT 测试",
|
||||
"Starting LocalDns service": "正在启动本地 DNS 服务",
|
||||
@@ -40,7 +38,6 @@
|
||||
"Add [ShadowsocksR] Server": "添加 [ShadowsocksR] 服务器",
|
||||
"Add [VMess] Server": "添加 [VMess] 服务器",
|
||||
"Add [Trojan] Server": "添加 [Trojan] 服务器",
|
||||
"VMess is currently not supported. For more information, please see our Github releases\n\nPress OK will redirect": "当前不支持 VMess 服务器。需要更多信息请查看我们的 Github 发布页\n\n点击 OK 将会跳转",
|
||||
"Netch is now minimized to the notification bar, double click this icon to restore.": "Netch 已最小化至通知栏,双击此图标恢复窗口。",
|
||||
"New version available": "发现新版本",
|
||||
"Mode": "模式",
|
||||
@@ -78,11 +75,8 @@
|
||||
"Update servers error from {0}": "从 {0} 更新服务器失败",
|
||||
|
||||
"Options": "选项",
|
||||
"Restart Service": "重启服务",
|
||||
"Restarting service": "正在重启服务中",
|
||||
"Service has been restarted": "服务已重启",
|
||||
"Uninstall Service": "卸载服务",
|
||||
"Uninstalling Service": "正在卸载服务中",
|
||||
"Uninstall NF Service": "卸载 NF 服务",
|
||||
"Uninstalling NF Service": "正在卸载 NF 服务中",
|
||||
"Service has been uninstalled": "服务已卸载",
|
||||
"Reload Modes": "重载模式",
|
||||
"Modes have been reload": "模式已重载",
|
||||
@@ -106,9 +100,11 @@
|
||||
|
||||
"Please press Stop button first": "请先点击停止按钮",
|
||||
"Please select a server first": "请先选择一个服务器",
|
||||
"Please select an mode first": "请先选择一个模式",
|
||||
"Please select a mode first": "请先选择一个模式",
|
||||
"Please enter a profile name first": "请先为该配置设置一个名称",
|
||||
"No saved profile here. Save a profile first by Ctrl+Click on the button": "当前按钮下没有保存配置,请先使用 CTRL + 左键 点击该按钮保存一个配置",
|
||||
"Remove this Profile?": "删除此配置?",
|
||||
"Profile Removed!": "配置已删除!",
|
||||
|
||||
"Used": "已使用",
|
||||
"Status": "状态",
|
||||
@@ -142,7 +138,6 @@
|
||||
"Remark can not be empty": "备注不可为空",
|
||||
"Link can not be empty": "链接不可为空",
|
||||
"Link must start with http:// or https://": "链接必须以 http:// 或 https:// 开头",
|
||||
"Successfully saved": "保存成功",
|
||||
"Please fill in alterID": "请填写额外ID",
|
||||
|
||||
"Settings": "设置",
|
||||
@@ -162,14 +157,15 @@
|
||||
"Global Bypass IPs": "全局直连 IP",
|
||||
"Port value illegal. Try again.": "端口值非法。请重试。",
|
||||
"Check update when opened": "打开软件时检查更新",
|
||||
"Start Shadowsocks from DLL (No support for ACL)": "SS DLL(推荐使用,不支持 ACL)",
|
||||
"Check Beta update": "检查 Beta 更新",
|
||||
"SS DLL(No ACL support)": "SS DLL(不支持 ACL)",
|
||||
"Modify System DNS": "修改系统 DNS",
|
||||
"ProfileCount": "快捷配置数量",
|
||||
"ProfileCount value illegal. Try again.": "快捷配置数值非法。请重试。",
|
||||
"STUN_ServerPort value illegal. Try again.": "STUN 端口数值非法。请重试。",
|
||||
"Detection interval value illegal. Try again.": "检测间隔值非法。请重试。",
|
||||
"TUN/TAP driver is not detected. Is it installed now?": "未检测到 TUN/TAP 驱动,是否现在安装?",
|
||||
"Failed to set the system proxy, it may be caused by the lack of dependent programs. Do you want to jump to Netch's official website to download dependent programs?": "设置系统代理失败,可能是缺少依赖导致,是否跳转 Netch 官网下载依赖程序?",
|
||||
"Experimental function": "实验性功能",
|
||||
"Delay test after start": "启动后延迟测试",
|
||||
"Enable": "启用",
|
||||
"Detection interval(sec)": "检测间隔(秒)",
|
||||
@@ -177,7 +173,6 @@
|
||||
"STUN Server Port": "STUN 服务器端口",
|
||||
"Custom ACL": "自定义 ACL 规则",
|
||||
"Language": "语言",
|
||||
"Saved.": "保存成功",
|
||||
|
||||
"Profile": "配置名",
|
||||
"Profiles": "配置",
|
||||
@@ -187,6 +182,8 @@
|
||||
"Exit": "退出",
|
||||
"Unable to start? Click me to download": "无法启动?点我下载依赖",
|
||||
|
||||
"The {0} port is in use.": "{0} 端口已被占用",
|
||||
|
||||
"Bypass LAN": "[网页代理] 绕过局域网",
|
||||
"Bypass LAN (Non System Proxy)": "[网页代理] 绕过局域网(不设置系统代理)",
|
||||
"Bypass LAN (TUN/TAP)": "[TUN/TAP] 绕过局域网",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Diagnostics.Tracing.Parsers;
|
||||
using Microsoft.Diagnostics.Tracing.Session;
|
||||
@@ -42,33 +43,33 @@ namespace Netch.Utils
|
||||
/// 根据程序名统计流量
|
||||
/// </summary>
|
||||
/// <param name="ProcessName"></param>
|
||||
public static void NetTraffic(Server server, Mode mode, MainController mainController)
|
||||
public static void NetTraffic(Server server, Mode mode, ref MainController mainController)
|
||||
{
|
||||
var counterLock = new object();
|
||||
//int sent = 0;
|
||||
|
||||
//var processList = Process.GetProcessesByName(ProcessName).Select(p => p.Id).ToHashSet();
|
||||
var processList = new List<int>();
|
||||
|
||||
if (server.Type.Equals("Socks5") && mainController.pModeController.AkaName == "HTTP")
|
||||
var instances = new List<Process>();
|
||||
if (server.Type.Equals("Socks5") && mainController.pModeController.Name == "HTTP")
|
||||
{
|
||||
processList.Add(((HTTPController) mainController.pModeController).pPrivoxyController.Instance.Id);
|
||||
instances.Add(((HTTPController) mainController.pModeController).pPrivoxyController.Instance);
|
||||
}
|
||||
else if (server.Type.Equals("SS") && Global.Settings.BootShadowsocksFromDLL)
|
||||
{
|
||||
processList.Add(Process.GetCurrentProcess().Id);
|
||||
instances.Add(Process.GetCurrentProcess());
|
||||
}
|
||||
else if (mainController.pEncryptedProxyController != null)
|
||||
{
|
||||
// mainController.pServerClientController.Instance
|
||||
processList.Add(mainController.pEncryptedProxyController.Instance.Id);
|
||||
instances.Add(mainController.pEncryptedProxyController.Instance);
|
||||
}
|
||||
else if (mainController.pModeController != null)
|
||||
{
|
||||
processList.Add(mainController.pModeController.Instance.Id);
|
||||
instances.Add(mainController.pModeController.Instance);
|
||||
}
|
||||
|
||||
Logging.Info("启动流量统计 PID:" + string.Join(",", processList.ToArray()));
|
||||
var processList = instances.Select(instance => instance.Id).ToList();
|
||||
|
||||
Logging.Info("流量统计进程:" + string.Join(",", instances.Select(instance => $"({instance.Id})"+instance.ProcessName).ToArray()));
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
@@ -101,18 +102,18 @@ namespace Netch.Utils
|
||||
}
|
||||
});
|
||||
|
||||
if ((Convert.ToInt32(MainForm.Instance.LastDownloadBandwidth) - Convert.ToInt32(received)) == 0)
|
||||
if ((Convert.ToInt32(Global.MainForm.LastDownloadBandwidth) - Convert.ToInt32(received)) == 0)
|
||||
{
|
||||
MainForm.Instance.OnBandwidthUpdated(0);
|
||||
Global.MainForm.OnBandwidthUpdated(0);
|
||||
received = 0;
|
||||
}
|
||||
|
||||
while (MainForm.Instance.State != State.Stopped)
|
||||
while (Global.MainForm.State != State.Stopped)
|
||||
{
|
||||
Task.Delay(1000).Wait();
|
||||
lock (counterLock)
|
||||
{
|
||||
MainForm.Instance.OnBandwidthUpdated(Convert.ToInt64(received));
|
||||
Global.MainForm.OnBandwidthUpdated(Convert.ToInt64(received));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,12 +69,12 @@ namespace Netch.Utils
|
||||
else
|
||||
{
|
||||
// 弹出提示
|
||||
MessageBoxX.Show("如果你是第一次使用本软件\n请务必前往http://netch.org 安装程序所需依赖,\n否则程序将无法正常运行!", i18N.Translate("注意!"));
|
||||
i18N.Load("System");
|
||||
// MessageBoxX.Show(i18N.Translate("If this is your first time using this software,\n please check https://netch.org to install supports first,\n or the program may report errors."));
|
||||
|
||||
// 创建 data 文件夹并保存默认设置
|
||||
Save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -45,5 +48,51 @@ namespace Netch.Utils
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 设置DNS
|
||||
/// </summary>
|
||||
/// <param name="dns"></param>
|
||||
public static void SetDNS(string[] dns)
|
||||
{
|
||||
ManagementClass wmi = new ManagementClass("Win32_NetworkAdapterConfiguration");
|
||||
ManagementObjectCollection moc = wmi.GetInstances();
|
||||
ManagementBaseObject inPar = null;
|
||||
ManagementBaseObject outPar = null;
|
||||
foreach (ManagementObject mo in moc)
|
||||
{
|
||||
//如果没有启用IP设置的网络设备则跳过,如果是虚拟机网卡也跳过
|
||||
if (!(bool)mo["IPEnabled"] ||
|
||||
mo["Description"].ToString().Contains("Virtual") ||
|
||||
mo["Description"].ToString().Contains("VMware") ||
|
||||
mo["Description"].ToString().Contains("TAP"))
|
||||
continue;
|
||||
|
||||
//设置DNS地址
|
||||
if (dns != null)
|
||||
{
|
||||
inPar = mo.GetMethodParameters("SetDNSServerSearchOrder");
|
||||
inPar["DNSServerSearchOrder"] = dns;
|
||||
outPar = mo.InvokeMethod("SetDNSServerSearchOrder", inPar, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 从网卡获取ip设置信息
|
||||
/// </summary>
|
||||
public static string[] getSystemDns()
|
||||
{
|
||||
var systemDns = new[] { "223.5.5.5", "1.1.1.1" };
|
||||
foreach (var network in NetworkInterface.GetAllNetworkInterfaces())
|
||||
if (!network.Description.Contains("Virtual") &&
|
||||
!network.Description.Contains("VMware") &&
|
||||
!network.Description.Contains("TAP") &&
|
||||
network.OperationalStatus == OperationalStatus.Up &&
|
||||
network.GetIPProperties()?.GatewayAddresses.Count != 0)
|
||||
{
|
||||
systemDns = network.GetIPProperties().DnsAddresses.Select(dns => dns.ToString()).ToArray();
|
||||
}
|
||||
|
||||
return systemDns;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NetFwTypeLib;
|
||||
|
||||
namespace Netch.Utils
|
||||
@@ -37,7 +38,14 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
public static void RemoveFwRules()
|
||||
{
|
||||
RemoveFwRules(NetchAutoRule);
|
||||
try
|
||||
{
|
||||
RemoveFwRules(NetchAutoRule);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Warning("添加防火墙规则错误\n" + e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -45,16 +53,23 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
public static void AddNetchFwRules()
|
||||
{
|
||||
if (GetFwRulePath(Netch).StartsWith(Global.NetchDir) && GetFwRulesNumber(Netch) >= ProgramPath.Length) return;
|
||||
RemoveNetchFwRules();
|
||||
foreach (var p in ProgramPath)
|
||||
try
|
||||
{
|
||||
var path = Path.GetFullPath(p);
|
||||
if (File.Exists(path))
|
||||
if (GetFwRulePath(Netch).StartsWith(Global.NetchDir) && GetFwRulesNumber(Netch) >= ProgramPath.Length) return;
|
||||
RemoveNetchFwRules();
|
||||
foreach (var p in ProgramPath)
|
||||
{
|
||||
AddFwRule("Netch", path);
|
||||
var path = Path.GetFullPath(p);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
AddFwRule("Netch", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Warning("添加防火墙规则错误(如已关闭防火墙则可无视此错误)\n" + e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -62,7 +77,15 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
private static void RemoveNetchFwRules()
|
||||
{
|
||||
RemoveFwRules(Netch);
|
||||
try
|
||||
{
|
||||
RemoveFwRules(Netch);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Warning("清除防火墙规则错误\n" + e);
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
#region 封装
|
||||
@@ -76,7 +99,7 @@ namespace Netch.Utils
|
||||
rule.Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW;
|
||||
// ApplicationName 大小不敏感
|
||||
rule.ApplicationName = exeFullPath;
|
||||
// rule.Description = "Used to block all internet access.";
|
||||
// rule.Description = "";
|
||||
rule.Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN;
|
||||
rule.Enabled = true;
|
||||
rule.InterfaceTypes = "All";
|
||||
@@ -87,17 +110,10 @@ namespace Netch.Utils
|
||||
|
||||
private static void RemoveFwRules(string ruleName)
|
||||
{
|
||||
try
|
||||
var c = GetFwRulesNumber(ruleName);
|
||||
foreach (var _ in new bool[c])
|
||||
{
|
||||
var c = GetFwRulesNumber(ruleName);
|
||||
foreach (var _ in new bool[c])
|
||||
{
|
||||
FwPolicy.Rules.Remove(ruleName);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Info("Netch 自带程序添加防火墙出错(如已关闭防火墙则可无视此错误)\n" + e);
|
||||
FwPolicy.Rules.Remove(ruleName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +127,8 @@ namespace Netch.Utils
|
||||
{
|
||||
try
|
||||
{
|
||||
var rule = (INetFwRule2)FwPolicy.Rules.Item(ruleName);
|
||||
return rule.ApplicationName;
|
||||
var rule = (INetFwRule2) FwPolicy.Rules.Item(ruleName);
|
||||
return rule.ApplicationName;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -122,14 +138,7 @@ namespace Netch.Utils
|
||||
|
||||
private static int GetFwRulesNumber(string ruleName)
|
||||
{
|
||||
// https://stackoverflow.com/a/53601691
|
||||
var i = 0;
|
||||
foreach (INetFwRule2 rule in FwPolicy.Rules)
|
||||
{
|
||||
if (rule.Name == ruleName)
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
return FwPolicy.Rules.Cast<INetFwRule2>().Count(rule => rule.Name == ruleName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public static class Logging
|
||||
{
|
||||
public const string LogFile = "logging\\application.log";
|
||||
|
||||
/// <summary>
|
||||
/// 信息
|
||||
@@ -12,16 +14,35 @@ namespace Netch.Utils
|
||||
/// <param name="text">内容</param>
|
||||
public static void Info(string text)
|
||||
{
|
||||
File.AppendAllText("logging\\application.log", $@"[{DateTime.Now}][INFO] {text}{Global.EOF}");
|
||||
Write(text, LogLevel.INFO);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 信息
|
||||
/// </summary>
|
||||
/// <param name="text">内容</param>
|
||||
public static void Warning(string text)
|
||||
{
|
||||
Write(text, LogLevel.WARNING);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
/// <param name="text">内容</param>
|
||||
public static void Error(string text)
|
||||
{
|
||||
File.AppendAllText("logging\\application.log", $@"[{DateTime.Now}][ERROR] {text}{Global.EOF}");
|
||||
Write(text, LogLevel.ERROR);
|
||||
}
|
||||
|
||||
private static readonly object FileLock = new object();
|
||||
|
||||
private static void Write(string text, LogLevel logLevel)
|
||||
{
|
||||
lock (FileLock)
|
||||
{
|
||||
File.AppendAllText(LogFile, $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Global.EOF}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Windows.Forms;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -8,17 +10,35 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
/// <param name="text">内容</param>
|
||||
/// <param name="title">自定义标题</param>
|
||||
/// <param name="info">弹窗等级 (标题, 图标)</param>
|
||||
/// <param name="level">弹窗等级 (标题, 图标)</param>
|
||||
/// <param name="confirm">需要确认</param>
|
||||
/// <param name="owner">阻止 owner Focus() 直到 Messageox 被关闭</param>
|
||||
public static DialogResult Show(string text, string title = "", bool info = true, bool confirm = false,IWin32Window owner = null)
|
||||
public static DialogResult Show(string text, LogLevel level = LogLevel.INFO, string title = "", bool confirm = false, IWin32Window owner = null)
|
||||
{
|
||||
MessageBoxIcon msgIcon;
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
title = level switch
|
||||
{
|
||||
LogLevel.INFO => "Information",
|
||||
LogLevel.WARNING => "Warning",
|
||||
LogLevel.ERROR => "Error",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
|
||||
};
|
||||
|
||||
msgIcon = level switch
|
||||
{
|
||||
LogLevel.INFO => MessageBoxIcon.Information,
|
||||
LogLevel.WARNING => MessageBoxIcon.Warning,
|
||||
LogLevel.ERROR => MessageBoxIcon.Exclamation,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
|
||||
};
|
||||
|
||||
return MessageBox.Show(
|
||||
owner: owner,
|
||||
text: i18N.Translate(text: text),
|
||||
caption: i18N.Translate(string.IsNullOrWhiteSpace(title) ? (info ? "Information" : "Error") : title),
|
||||
text: text,
|
||||
caption: i18N.Translate(title),
|
||||
buttons: confirm ? MessageBoxButtons.OKCancel : MessageBoxButtons.OK,
|
||||
icon: info ? MessageBoxIcon.Information : MessageBoxIcon.Exclamation);
|
||||
icon: msgIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
71
Netch/Utils/OnlyInstance.cs
Normal file
71
Netch/Utils/OnlyInstance.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public class OnlyInstance
|
||||
{
|
||||
public enum Commands
|
||||
{
|
||||
Show,
|
||||
Exit
|
||||
}
|
||||
|
||||
public static event EventHandler<Commands> Called;
|
||||
|
||||
private static void OnCalled(Commands e)
|
||||
{
|
||||
Called?.Invoke(null, e);
|
||||
}
|
||||
|
||||
public static async void Server()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PortHelper.PortInUse(Global.Settings.UDPSocketPort))
|
||||
{
|
||||
Global.Settings.UDPSocketPort = PortHelper.GetAvailablePort();
|
||||
Configuration.Save();
|
||||
}
|
||||
|
||||
var data = new byte[1024];
|
||||
var newsock = new UdpClient(new IPEndPoint(IPAddress.Loopback, Global.Settings.UDPSocketPort));
|
||||
|
||||
while (true)
|
||||
{
|
||||
var result = await newsock.ReceiveAsync();
|
||||
data = result.Buffer;
|
||||
if (Enum.TryParse<Commands>(Encoding.ASCII.GetString(data, 0, data.Length), out var command))
|
||||
{
|
||||
OnCalled(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public static async void Send(Commands command)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var udpClient = new UdpClient(Global.Settings.UDPSocketPort))
|
||||
{
|
||||
udpClient.Connect(IPAddress.Loopback, Global.Settings.UDPSocketPort);
|
||||
var sendBytes = Encoding.ASCII.GetBytes(command.ToString());
|
||||
await udpClient.SendAsync(sendBytes, sendBytes.Length);
|
||||
|
||||
udpClient.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
139
Netch/Utils/PortHelper.cs
Normal file
139
Netch/Utils/PortHelper.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using Netch.Controllers;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public static class PortHelper
|
||||
{
|
||||
private static readonly List<int[]> TCPExcludedRanges = new List<int[]>();
|
||||
private static readonly List<int[]> UDPExcludedRanges = new List<int[]>();
|
||||
|
||||
static PortHelper()
|
||||
{
|
||||
try
|
||||
{
|
||||
GetExcludedPortRange(PortType.TCP, ref TCPExcludedRanges);
|
||||
GetExcludedPortRange(PortType.UDP, ref UDPExcludedRanges);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error("获取保留端口失败: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetExcludedPortRange(PortType portType, ref List<int[]> targetList)
|
||||
{
|
||||
var lines = new List<string>();
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "netsh",
|
||||
Arguments = $" int ipv4 show excludedportrange {portType}",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
process.OutputDataReceived += (s, e) =>
|
||||
{
|
||||
if (e.Data != null) lines.Add(e.Data);
|
||||
};
|
||||
process.Start();
|
||||
process.BeginOutputReadLine();
|
||||
process.WaitForExit();
|
||||
|
||||
var splitLine = false;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (!splitLine)
|
||||
{
|
||||
if (line.StartsWith("-"))
|
||||
{
|
||||
splitLine = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line == string.Empty)
|
||||
break;
|
||||
|
||||
var value = line.Trim().Split(' ').Where(s => s != string.Empty);
|
||||
|
||||
var port = 0;
|
||||
var _ = (from s1 in value
|
||||
where int.TryParse(s1, out port)
|
||||
select port).ToArray();
|
||||
|
||||
targetList.Add(_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查端口是否是保留端口
|
||||
/// </summary>
|
||||
/// <param name="port">端口</param>
|
||||
/// <param name="type">端口类型</param>
|
||||
/// <returns>是否是保留端口</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
private static bool IsPortExcluded(int port, PortType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
PortType.TCP => TCPExcludedRanges.Any(range => range[0] <= port && port <= range[1]),
|
||||
PortType.UDP => UDPExcludedRanges.Any(range => range[0] <= port && port <= range[1]),
|
||||
PortType.Both => IsPortExcluded(port, PortType.TCP) || IsPortExcluded(port, PortType.UDP),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定类型的端口是否已经被使用了
|
||||
/// </summary>
|
||||
/// <param name="port">端口</param>
|
||||
/// <param name="type">检查端口类型</param>
|
||||
/// <returns>是否被占用</returns>
|
||||
public static bool PortInUse(int port, PortType type = PortType.Both)
|
||||
{
|
||||
var netInfo = IPGlobalProperties.GetIPGlobalProperties();
|
||||
var isTcpUsed = type != PortType.UDP &&
|
||||
(IsPortExcluded(port, PortType.TCP) ||
|
||||
netInfo.GetActiveTcpListeners().Any(ipEndPoint => ipEndPoint.Port == port));
|
||||
var isUdpUsed = type != PortType.TCP &&
|
||||
(IsPortExcluded(port, PortType.UDP) ||
|
||||
netInfo.GetActiveUdpListeners().Any(ipEndPoint => ipEndPoint.Port == port));
|
||||
var isPortExcluded = !MainController.UsingPorts.Contains(port);
|
||||
|
||||
return isPortExcluded && (isTcpUsed || isUdpUsed);
|
||||
}
|
||||
|
||||
public static int GetAvailablePort()
|
||||
{
|
||||
for (var i = 0; i < 55535; i++)
|
||||
{
|
||||
var p = new Random().Next(10000, 65535);
|
||||
if (!PortInUse(p))
|
||||
{
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Cant Generate Available Port");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查端口类型
|
||||
/// </summary>
|
||||
public enum PortType
|
||||
{
|
||||
TCP,
|
||||
UDP,
|
||||
Both
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -15,21 +16,28 @@ namespace Netch.Utils
|
||||
/// <returns>适配器 ID</returns>
|
||||
public static string GetComponentID()
|
||||
{
|
||||
var adaptersRegistry = Registry.LocalMachine.OpenSubKey(ADAPTER_KEY);
|
||||
|
||||
foreach (var adapterRegistryName in adaptersRegistry.GetSubKeyNames())
|
||||
try
|
||||
{
|
||||
if (adapterRegistryName != "Configuration" && adapterRegistryName != "Properties")
|
||||
{
|
||||
var adapterRegistry = adaptersRegistry.OpenSubKey(adapterRegistryName);
|
||||
var adaptersRegistry = Registry.LocalMachine.OpenSubKey(ADAPTER_KEY);
|
||||
|
||||
var adapterComponentId = adapterRegistry.GetValue("ComponentId", "").ToString();
|
||||
if (adapterComponentId == TUNTAP_COMPONENT_ID_0901 || adapterComponentId == TUNTAP_COMPONENT_ID_0801)
|
||||
foreach (var adapterRegistryName in adaptersRegistry.GetSubKeyNames())
|
||||
{
|
||||
if (adapterRegistryName != "Configuration" && adapterRegistryName != "Properties")
|
||||
{
|
||||
return adapterRegistry.GetValue("NetCfgInstanceId", "").ToString();
|
||||
var adapterRegistry = adaptersRegistry.OpenSubKey(adapterRegistryName);
|
||||
|
||||
var adapterComponentId = adapterRegistry.GetValue("ComponentId", "").ToString();
|
||||
if (adapterComponentId == TUNTAP_COMPONENT_ID_0901 || adapterComponentId == TUNTAP_COMPONENT_ID_0801)
|
||||
{
|
||||
return adapterRegistry.GetValue("NetCfgInstanceId", "").ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Warning(e.ToString());
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
@@ -55,4 +63,4 @@ namespace Netch.Utils
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,11 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -11,17 +14,16 @@ namespace Netch.Utils
|
||||
{
|
||||
public static class Utils
|
||||
{
|
||||
public static bool OpenUrl(string path)
|
||||
public static bool Open(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
new Process
|
||||
Process.Start(new ProcessStartInfo()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo(path)
|
||||
{
|
||||
UseShellExecute = true
|
||||
}
|
||||
}.Start();
|
||||
FileName = "explorer.exe",
|
||||
Arguments = path,
|
||||
UseShellExecute = true
|
||||
});
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
@@ -30,23 +32,6 @@ namespace Netch.Utils
|
||||
}
|
||||
}
|
||||
|
||||
public static bool OpenDir(string dir)
|
||||
{
|
||||
if (Directory.Exists(dir))
|
||||
{
|
||||
try
|
||||
{
|
||||
return OpenUrl(dir);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static async Task<int> TCPingAsync(IPAddress ip, int port, int timeout = 1000, CancellationToken ct = default)
|
||||
{
|
||||
using var client = new TcpClient(ip.AddressFamily);
|
||||
@@ -63,6 +48,7 @@ namespace Netch.Utils
|
||||
var t = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
|
||||
return t;
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@@ -72,6 +58,7 @@ namespace Netch.Utils
|
||||
{
|
||||
Hostname = Hostname.Split(':')[0];
|
||||
}
|
||||
|
||||
string Country;
|
||||
try
|
||||
{
|
||||
@@ -87,7 +74,7 @@ namespace Netch.Utils
|
||||
|
||||
if (DnsResult != null)
|
||||
{
|
||||
Country = databaseReader.Country(Hostname).Country.IsoCode;
|
||||
Country = databaseReader.Country(DnsResult).Country.IsoCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -99,7 +86,22 @@ namespace Netch.Utils
|
||||
{
|
||||
Country = "Unknown";
|
||||
}
|
||||
|
||||
return Country == null ? "Unknown" : Country;
|
||||
}
|
||||
|
||||
public static string SHA256CheckSum(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var SHA256 = SHA256Managed.Create();
|
||||
var fileStream = File.OpenRead(filePath);
|
||||
return SHA256.ComputeHash(fileStream).Aggregate(string.Empty, (current, b) => current + b.ToString("x2"));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
Netch/Utils/WebUtil.cs
Normal file
76
Netch/Utils/WebUtil.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public class WebUtil
|
||||
{
|
||||
private const string DefaultUserAgent = @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36";
|
||||
|
||||
private static int DefaultGetTimeout => Global.Settings.RequestTimeout;
|
||||
|
||||
public static HttpWebRequest CreateRequest(string url, int? timeout = null, string userAgent = null)
|
||||
{
|
||||
var req = (HttpWebRequest) WebRequest.Create(url);
|
||||
req.UserAgent = string.IsNullOrEmpty(userAgent) ? DefaultUserAgent : userAgent;
|
||||
req.Accept = "*/*";
|
||||
req.KeepAlive = true;
|
||||
req.Timeout = timeout ?? DefaultGetTimeout;
|
||||
req.ReadWriteTimeout = timeout ?? DefaultGetTimeout;
|
||||
req.Headers.Add("Accept-Charset", "utf-8");
|
||||
return req;
|
||||
}
|
||||
|
||||
/// <param name="req"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="WebException"></exception>
|
||||
public static async Task<string> DownloadStringAsync(HttpWebRequest req)
|
||||
{
|
||||
string content;
|
||||
var response = (HttpWebResponse) await req.GetResponseAsync();
|
||||
using (var responseStream = response.GetResponseStream())
|
||||
{
|
||||
using (var sr = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")))
|
||||
{
|
||||
content = await sr.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
|
||||
response.Close();
|
||||
return content;
|
||||
}
|
||||
|
||||
/// <param name="req"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="WebException"></exception>
|
||||
public static string DownloadString(HttpWebRequest req)
|
||||
{
|
||||
string content;
|
||||
var response = (HttpWebResponse) req.GetResponse();
|
||||
using (var responseStream = response.GetResponseStream())
|
||||
{
|
||||
using (var sr = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")))
|
||||
{
|
||||
content = sr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
response.Close();
|
||||
return content;
|
||||
}
|
||||
|
||||
/// <param name="req"></param>
|
||||
/// <param name="fileFullPath"></param>
|
||||
/// <exception cref="WebException"></exception>
|
||||
public static async Task DownloadFileAsync(HttpWebRequest req, string fileFullPath)
|
||||
{
|
||||
var dir = Path.GetDirectoryName(fileFullPath) ?? throw new ArgumentException();
|
||||
if (!Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
File.WriteAllText(fileFullPath, await DownloadStringAsync(req));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace Netch.Utils
|
||||
public static void Load(string langCode)
|
||||
{
|
||||
LangCode = langCode;
|
||||
|
||||
|
||||
var text = "";
|
||||
if (langCode.Equals("System"))
|
||||
{
|
||||
@@ -34,8 +34,6 @@ namespace Netch.Utils
|
||||
langCode = CultureInfo.CurrentCulture.Name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (langCode == "zh-CN")
|
||||
{
|
||||
// 尝试加载内置中文语言
|
||||
@@ -69,18 +67,30 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
/// <param name="text">需要翻译的文本</param>
|
||||
/// <returns>翻译完毕的文本</returns>
|
||||
public static string Translate(string text)
|
||||
{
|
||||
return Data.Contains(text) ? Data[text].ToString() : text;
|
||||
}
|
||||
public static string Translate(params string[] text)
|
||||
public static string Translate(params object[] text)
|
||||
{
|
||||
var a = new StringBuilder();
|
||||
foreach (var t in text)
|
||||
a.Append(Data.Contains(t) ? Data[t].ToString() : t);
|
||||
if (t is string)
|
||||
a.Append(Data.Contains(t) ? Data[t].ToString() : t);
|
||||
else
|
||||
a.Append(t);
|
||||
return a.ToString();
|
||||
}
|
||||
|
||||
|
||||
public static string TranslateFormat(string format, params object[] args)
|
||||
{
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i] is string)
|
||||
{
|
||||
args[i] = Translate((string) args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Format(Translate(format), args);
|
||||
}
|
||||
|
||||
public static List<string> GetTranslateList()
|
||||
{
|
||||
var translateFile = new List<string> {"System", "zh-CN", "en-US"};
|
||||
|
||||
2
binaries
2
binaries
Submodule binaries updated: 841b75af2f...422c811311
Submodule translations updated: e7c5263f75...06e77dc2b1
Reference in New Issue
Block a user