Compare commits

...

52 Commits

Author SHA1 Message Date
ChsBuffer
26cde4a8bb 更新NetchCore.dll, 移除安装运行库提示 2020-08-09 09:03:21 +08:00
ChsBuffer
e5e7892741 扫描文件夹异常处理,主控制器异常处理改进 2020-08-09 05:41:50 +08:00
ChsBuffer
764009ebff 代码清理 2020-08-09 03:54:57 +08:00
ChsBuffer
0b09ce62f1 修复出现异常后被禁用的界面不被再启用,恢复子进程启动优先级为正常 2020-08-09 02:22:30 +08:00
ChsBuffer
3d24aa7583 控制器日志写入线程安全,主控制器启动停止等改为异步执行,修复主控制器停止提前释放 2020-08-08 00:31:40 +08:00
ChsBuffer
689f29d0f0 使主界面常用UI操作方法线程安全 2020-08-07 22:33:41 +08:00
ChsBuffer
715d25a6f6 NetTraffic() 主控制器引用传递,补充模式注释,启动只检查需要用的端口,补充属性可访问性 2020-08-07 22:32:01 +08:00
Amazing_DM
0d1762eebe Update submodule 2020-08-07 11:50:56 +08:00
ChsBuffer
8f7f2e3d1c 修复主控制器启动阻塞UI线程,保留设置来重新生成状态栏端口信息 2020-08-07 11:22:13 +08:00
ChsBuffer
3311115bda 修复已启动更新ACL会将状态设为等待,更新订阅、ACL、启动等更改为异步执行 2020-08-07 02:22:45 +08:00
ChsBuffer
d447f963df 监听UDP接收指令,实现再次启动则弹出已启动的 2020-08-06 05:50:43 +08:00
ChsBuffer
0e2d3b63b7 端口可用性检查检查是否是保留端口 2020-08-06 04:19:15 +08:00
ChsBuffer
6d85c78552 进程停止后关闭 _writeStreamTimer 2020-08-05 16:18:23 +08:00
Connection Refused
3883239836 Update MainForm.Status.cs 2020-08-03 01:18:16 +08:00
Connection Refused
2e0655a2a8 Update zh-CN 2020-08-03 01:15:30 +08:00
ChsBuffer
11be70d352 Controller 易用性、兼容性改进 2020-08-02 05:58:30 +08:00
ChsBuffer
46dfc885a2 修复首次启动Netch开启启动后测试延迟,启动后会跳回第一个服务器 2020-08-01 01:47:49 +08:00
ChsBuffer
5495b94513 优化日志事件接收方法, 修复启动控制器时没有清除上次启动的日志 2020-07-30 21:31:25 +08:00
ChsBuffer
4e5bdab6b8 自动构建缓存Nuget包
计算主程序SHA256
2020-07-30 14:07:24 +08:00
ChsBuffer
f61827a575 实现WebUtil
Beta 更新检查
订阅更新异常提示
2020-07-30 02:36:42 +08:00
ChsBuffer
87adf4de1e fix Check SHA256 sum 2020-07-30 00:27:21 +08:00
ChsBuffer
3488e7bbf3 捕捉主控制器异常 2020-07-29 22:33:01 +08:00
ChsBuffer
8c9953a5d4 适配器检查异常处理 2020-07-29 14:37:29 +08:00
ChsBuffer
aef6be2c80 修复网页代理可能不恢复原设置或不取消设置代理 2020-07-29 14:24:23 +08:00
ChsBuffer
c9396bd6b2 捕捉到未处理错误打开日志
减少TUN/TAP设置路由表代码
日志细节优化
2020-07-28 16:10:26 +08:00
ChsBuffer
b6a3f5bc36 系统代理设置 LAN ip 直连 2020-07-28 06:14:21 +08:00
ChsBuffer
bbf9c5ed7c 尝试修复退出错误
修复停止时进程堵塞
2020-07-25 04:02:01 +08:00
ChsBuffer
6174e78696 输出重定向的文本编码转换 2020-07-25 00:47:50 +08:00
ChsBuffer
75045fe1a6 remove "Pre Build CI.yml" 2020-07-23 16:33:49 +08:00
ChsBuffer
3da7451b9f 增加修改系统DNS开关
补充"卸载服务"为"卸载NF服务"
2020-07-23 16:32:18 +08:00
ChsBuffer
27d68163a9 只有模式4(系统代理模式)设置系统代理 2020-07-23 16:32:13 +08:00
AmazingDM
b1c0990ca5 Create Pre Build CI.yml 2020-07-20 14:21:45 +08:00
ChsBuffer
7181db0376 更新翻译 2020-07-19 19:04:48 +08:00
ChsBuffer
7a15fa7375 修复 端口被占用文本错误,
修复 停止并退出的逻辑错误
2020-07-19 18:18:57 +08:00
ChsBuffer
e10c994e38 Merge pull request #318 from chsbuffer/dev
多项改进,Bug修复
2020-07-19 14:13:20 +08:00
ChsBuffer
54a263ad06 改进已启动时设置端口的占用检查处理 2020-07-19 03:41:19 +08:00
ChsBuffer
8a56dd4582 清理翻译,调整配置按钮点击逻辑,移除系统位元检查 2020-07-19 02:17:32 +08:00
ChsBuffer
cd891cec33 防火墙类异常处理 2020-07-19 02:17:32 +08:00
ChsBuffer
635a033434 改进端口检查 2020-07-19 02:17:32 +08:00
ChsBuffer
6fd3aa48a5 停止时同时结束加密代理和模式 2020-07-19 02:17:31 +08:00
Amazing_DM
a319833bd5 使用进程模式时网卡DNS将自动替换为1.1.1.1和8.8.8.8,关闭后自动恢复
更新驱动文件1.5.9.3
2020-07-18 14:48:23 +08:00
AmazingDM
70e5d8324e Merge pull request #317 from chsbuffer/dev
更新 Controller类,改善错误输出
2020-07-17 19:29:31 +08:00
ChsBuffer
1cecccb173 尝试杀子进程后进行端口检查 2020-07-17 19:23:12 +08:00
ChsBuffer
281c67aced 更新 Controller类
改善错误输出
2020-07-17 19:18:29 +08:00
Amazing_DM
a779295525 fix a bug 2020-07-17 10:55:41 +08:00
AmazingDM
e665850fc9 Merge pull request #316 from Wangrui-i/master
修复端口被占用的提示没有翻译的问题;软件首次启动提示 增加翻译(翻译为当前系统语言)
2020-07-17 10:15:55 +08:00
橘子皮
2537fdd8fe 修复端口被占用的提示没有翻译的问题;软件首次启动提示 增加翻译(翻译为当前系统语言) 2020-07-17 10:03:31 +08:00
AmazingDM
cc459d3f59 Merge pull request #314 from chsbuffer/master
细节修缮
2020-07-17 00:45:32 +08:00
AmazingDM
7c051413d3 Merge branch 'master' into master 2020-07-17 00:45:19 +08:00
AmazingDM
17165e4623 Merge pull request #315 from Wangrui-i/master
新增端口占用检查 #307
2020-07-17 00:25:01 +08:00
橘子皮
a6fd0764e1 新增端口占用检查 #307 2020-07-17 00:12:25 +08:00
ChsBuffer
d27c7c016c 细节修缮 2020-07-16 20:24:02 +08:00
53 changed files with 2369 additions and 2156 deletions

View File

@@ -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'

View File

@@ -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()

View File

@@ -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;
}
}
}
}

View File

@@ -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()

View File

@@ -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()

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View 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);
}
}
}
}

View File

@@ -1,3 +1,4 @@
using System.Threading.Tasks;
using Netch.Models;
namespace Netch.Controllers

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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)
};
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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()

View File

@@ -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;
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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;

View File

@@ -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
}
}

View File

@@ -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);
}
}
}

View File

@@ -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");
}
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
}
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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>

View File

@@ -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;

View File

@@ -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> &lt;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
View File

@@ -0,0 +1,9 @@
namespace Netch.Models
{
public enum LogLevel
{
INFO,
WARNING,
ERROR
}
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -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>
/// 设置直连

View File

@@ -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);
}
}
}
}

View File

@@ -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" />

View File

@@ -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;
}
}
}

View File

@@ -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] 绕过局域网",

View File

@@ -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));
}
}
}

View File

@@ -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>

View File

@@ -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;
}
}
}
}

View File

@@ -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

View File

@@ -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}");
}
}
}
}
}

View File

@@ -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);
}
}
}

View 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
View 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
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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
View 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));
}
}
}

View File

@@ -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"};