基本完成重构 Controller

This commit is contained in:
ChsBuffer
2020-07-11 19:30:35 +08:00
parent d2f886ef4b
commit 9f27c4bcf9
27 changed files with 806 additions and 774 deletions

View File

@@ -1,6 +1,5 @@
using System;
using System.Diagnostics;
using Netch.Forms;
using Netch.Utils;
namespace Netch.Controllers
@@ -9,9 +8,10 @@ namespace Netch.Controllers
{
public DNSController()
{
MainName = "unbound";
AkaName = "dns Service";
MainFile = "unbound";
ExtFiles = new[] {"unbound-service.conf", "forward-zone.conf"};
ready = BeforeStartProgress();
InitCheck();
}
/// <summary>
@@ -20,33 +20,36 @@ namespace Netch.Controllers
/// <returns></returns>
public bool Start()
{
MainForm.Instance.StatusText(i18N.Translate("Starting dns Service"));
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 = MainController.GetProcess("bin\\unbound.exe");
Instance.StartInfo.Arguments = "-c unbound-service.conf -v";
Instance.OutputDataReceived += OnOutputDataReceived;
Instance.ErrorDataReceived += OnOutputDataReceived;
Instance.Start();
Instance.BeginOutputReadLine();
Instance.BeginErrorReadLine();
Logging.Info("dns-unbound 启动完毕");
return true;
}
catch (Exception)
catch (Exception e)
{
Logging.Info("dns-unbound 进程出错");
Stop();
Logging.Error("dns-unbound 进程出错\n" + e);
return false;
}
}
public void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
{
WriteLog(e);
}
public override void Stop()
{
StopInstance();
}
}
}

View File

@@ -6,12 +6,12 @@ using Netch.Utils;
namespace Netch.Controllers
{
public class SSController : ServerClient
public class SSController : EncryptedProxy
{
public SSController()
{
MainName = "Shadowsocks";
ready = BeforeStartProgress();
MainFile = "Shadowsocks";
InitCheck();
}
public override bool Start(Server server, Mode mode)
@@ -27,7 +27,7 @@ namespace Netch.Controllers
if (!NativeMethods.Shadowsocks.Info(client, remote, passwd, method))
{
State = State.Stopped;
Logging.Info("DLL SS INFO 设置失败!");
Logging.Error("DLL SS INFO 设置失败!");
return false;
}
@@ -36,7 +36,7 @@ namespace Netch.Controllers
if (!NativeMethods.Shadowsocks.Start())
{
State = State.Stopped;
Logging.Info("DLL SS 启动失败!");
Logging.Error("DLL SS 启动失败!");
return false;
}
@@ -45,18 +45,13 @@ namespace Netch.Controllers
return true;
}
Instance = MainController.GetProcess("bin\\Shadowsocks.exe");
#region Instance.Arguments
if (!Ready) return false;
Instance = GetProcess("bin\\Shadowsocks.exe");
Instance.StartInfo.Arguments = $"-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 = $"-s {server.Hostname} -p {server.Port} -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u --plugin {server.Plugin} --plugin-opts \"{server.PluginOption}\"";
else
Instance.StartInfo.Arguments = $"-s {server.Hostname} -p {server.Port} -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u";
Instance.StartInfo.Arguments += $" --plugin {server.Plugin} --plugin-opts \"{server.PluginOption}\"";
if (mode.BypassChina) Instance.StartInfo.Arguments += " --acl default.acl";
#endregion
Instance.OutputDataReceived += OnOutputDataReceived;
Instance.ErrorDataReceived += OnOutputDataReceived;
@@ -64,6 +59,7 @@ namespace Netch.Controllers
Instance.Start();
Instance.BeginOutputReadLine();
Instance.BeginErrorReadLine();
for (var i = 0; i < 1000; i++)
{
Thread.Sleep(10);
@@ -72,14 +68,14 @@ namespace Netch.Controllers
if (State == State.Stopped)
{
Logging.Info("SS 进程启动失败");
Logging.Error("SS 进程启动失败");
Stop();
return false;
}
}
Logging.Info("SS 进程启动超时");
Logging.Error("SS 进程启动超时");
Stop();
return false;
}
@@ -87,10 +83,11 @@ namespace Netch.Controllers
/// <summary>
/// SSController 停止
/// </summary>
public new void Stop()
public override void Stop()
{
base.Stop();
if (Global.Settings.BootShadowsocksFromDLL) NativeMethods.Shadowsocks.Stop();
else
StopInstance();
}
public override void OnOutputDataReceived(object sender, DataReceivedEventArgs e)

View File

@@ -5,23 +5,23 @@ using Netch.Utils;
namespace Netch.Controllers
{
public class SSRController : ServerClient
public class SSRController : EncryptedProxy
{
public SSRController()
{
MainName = "ShadowsocksR";
ready = BeforeStartProgress();
MainFile = "ShadowsocksR";
InitCheck();
}
public override bool Start(Server server, Mode mode)
{
Instance = MainController.GetProcess("bin\\ShadowsocksR.exe");
if (!Ready) return false;
Instance = GetProcess("bin\\ShadowsocksR.exe");
Instance.StartInfo.FileName = "bin\\ShadowsocksR.exe";
Instance.OutputDataReceived += OnOutputDataReceived;
Instance.ErrorDataReceived += OnOutputDataReceived;
#region Instance.Arguments
Instance.StartInfo.Arguments = $"-s {server.Hostname} -p {server.Port} -k \"{server.Password}\" -m {server.EncryptMethod} -t 120";
if (!string.IsNullOrEmpty(server.Protocol))
@@ -42,8 +42,6 @@ namespace Netch.Controllers
if (mode.BypassChina) Instance.StartInfo.Arguments += " --acl default.acl";
#endregion
State = State.Starting;
Instance.Start();
Instance.BeginOutputReadLine();
@@ -57,14 +55,14 @@ namespace Netch.Controllers
if (State == State.Stopped)
{
Logging.Info("SSR 进程启动失败");
Logging.Error("SSR 进程启动失败");
Stop();
return false;
}
}
Logging.Info("SSR 进程启动超时");
Logging.Error("SSR 进程启动超时");
Stop();
return false;
}
@@ -81,5 +79,10 @@ namespace Netch.Controllers
else if (e.Data.Contains("Invalid config path") || e.Data.Contains("usage")) State = State.Stopped;
}
}
public override void Stop()
{
StopInstance();
}
}
}

View File

@@ -2,24 +2,23 @@
using System.Diagnostics;
using System.IO;
using System.Threading;
using Netch.Forms;
using Netch.Models;
using Netch.Utils;
using Newtonsoft.Json;
namespace Netch.Controllers
{
public class TrojanController : ServerClient
public class TrojanController : EncryptedProxy
{
public TrojanController()
{
MainName = "Trojan";
ready = BeforeStartProgress();
MainFile = "Trojan";
InitCheck();
}
public override bool Start(Server server, Mode mode)
{
MainForm.Instance.StatusText(i18N.Translate("Starting Trojan"));
if (!Ready) return false;
File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new Trojan
{
@@ -33,7 +32,7 @@ namespace Netch.Controllers
}
}));
Instance = MainController.GetProcess("bin\\Trojan.exe");
Instance = GetProcess("bin\\Trojan.exe");
Instance.StartInfo.Arguments = "-c ..\\data\\last.json";
Instance.OutputDataReceived += OnOutputDataReceived;
Instance.ErrorDataReceived += OnOutputDataReceived;
@@ -50,14 +49,14 @@ namespace Netch.Controllers
if (State == State.Stopped)
{
Logging.Info("Trojan 进程启动失败");
Logging.Error("Trojan 进程启动失败");
Stop();
return false;
}
}
Logging.Info("Trojan 进程启动超时");
Logging.Error("Trojan 进程启动超时");
Stop();
return false;
}
@@ -74,5 +73,10 @@ namespace Netch.Controllers
else if (e.Data.Contains("exiting")) State = State.Stopped;
}
}
public override void Stop()
{
StopInstance();
}
}
}

View File

@@ -9,16 +9,17 @@ using VMess = Netch.Models.Information.VMess;
namespace Netch.Controllers
{
public class VMessController : ServerClient
public class VMessController : EncryptedProxy
{
public VMessController()
{
MainName = "v2ray";
ready = BeforeStartProgress();
MainFile = "v2ray";
InitCheck();
}
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>
@@ -167,7 +168,7 @@ namespace Netch.Controllers
}
}));
Instance = MainController.GetProcess("bin\\v2ray.exe");
Instance = GetProcess("bin\\v2ray.exe");
Instance.StartInfo.Arguments = "-config ..\\data\\last.json";
Instance.OutputDataReceived += OnOutputDataReceived;
@@ -190,14 +191,14 @@ namespace Netch.Controllers
if (State == State.Stopped)
{
Logging.Info("V2Ray 进程启动失败");
Logging.Error("V2Ray 进程启动失败");
Stop();
return false;
}
}
Logging.Info("V2Ray 进程启动超时");
Logging.Error("V2Ray 进程启动超时");
Stop();
return false;
}
@@ -214,5 +215,10 @@ namespace Netch.Controllers
else if (e.Data.Contains("config file not readable") || e.Data.Contains("failed to")) State = State.Stopped;
}
}
public override void Stop()
{
StopInstance();
}
}
}

View File

@@ -6,8 +6,18 @@ using Netch.Utils;
namespace Netch.Controllers
{
public class Controller
public abstract class Controller
{
/// <summary>
/// 控制器名
/// <param />
/// 未赋值会在 <see cref="InitCheck" /> 赋值为 <see cref="MainFile" />
/// </summary>
public string AkaName;
/// <summary>
/// 其他需要文件
/// </summary>
protected string[] ExtFiles;
/// <summary>
@@ -16,21 +26,26 @@ namespace Netch.Controllers
public Process Instance;
/// <summary>
/// 程序名
/// 程序名(不含扩展名)
/// </summary>
public string MainName;
public string MainFile;
public bool ready;
/// <summary>
/// 运行检查, 由 <see cref="InitCheck()" /> 赋值
/// </summary>
public bool Ready;
/// <summary>
/// 当前状态
/// </summary>
protected State State = State.Waiting;
public abstract void Stop();
/// <summary>
/// 停止
/// </summary>
public void Stop()
protected void StopInstance()
{
try
{
@@ -40,7 +55,7 @@ namespace Netch.Controllers
}
catch (Exception e)
{
Logging.Info(e.ToString());
Logging.Error($"停止 {MainFile}.exe 错误:\n" + e);
}
}
@@ -49,53 +64,87 @@ namespace Netch.Controllers
/// 杀残留进程,清除日志,检查文件
/// </summary>
/// <returns></returns>
protected bool BeforeStartProgress()
protected void InitCheck()
{
if (string.IsNullOrEmpty(AkaName)) AkaName = MainFile;
var result = false;
// 杀残留
MainController.KillProcessByName(MainName);
MainController.KillProcessByName(MainFile);
// 清日志
try
{
if (File.Exists($"logging\\{MainName}.log")) File.Delete($"logging\\{MainName}.log");
if (File.Exists($"logging\\{AkaName}.log")) File.Delete($"logging\\{AkaName}.log");
}
catch (Exception)
{
// ignore
// ignored
}
// 检查文件
if (!File.Exists($"bin\\{MainName}.exe")) Logging.Info($"bin\\{MainName}.exe 文件不存在");
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)
result = true;
extResult = true;
else
foreach (var f in ExtFiles)
if (!File.Exists($"bin\\{f}"))
Logging.Info($"bin\\{f}.exe 文件不存在");
{
extResult = false;
Logging.Error($"附加文件 bin\\{f} 不存在");
}
if (!ready) Logging.Info(MainName + "未能就绪");
return result;
result = extResult && mainResult;
if (!result)
Logging.Error(AkaName + " 未就绪");
Ready = result;
}
/// <summary>
/// 写日志
/// </summary>
/// <param name="e"></param>
/// <returns>e是否为空</returns>
public bool WriteLog(DataReceivedEventArgs e)
/// <param name="std"></param>
/// <returns><see cref="std" />是否为空</returns>
protected bool WriteLog(DataReceivedEventArgs std)
{
if (string.IsNullOrWhiteSpace(e.Data)) return false;
if (string.IsNullOrWhiteSpace(std.Data)) return false;
try
{
File.AppendAllText($"logging\\{MainName}.log", $@"{e.Data}{Global.EOF}");
File.AppendAllText($"logging\\{AkaName}.log", $@"{std.Data}{Global.EOF}");
}
catch (Exception exception)
catch (Exception e)
{
Logging.Info($"写入{MainName}日志失败" + exception);
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;
}
}
}

View File

@@ -0,0 +1,18 @@
using System.Diagnostics;
using Netch.Models;
namespace Netch.Controllers
{
public abstract class EncryptedProxy : Controller
{
/// <summary>
/// 启动
/// </summary>
/// <param name="server">服务器</param>
/// <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,15 @@
using Netch.Models;
namespace Netch.Controllers
{
public abstract class ModeController : Controller
{
/// <summary>
/// 启动
/// </summary>
/// <param name="server">服务器</param>
/// <param name="mode">模式</param>
/// <returns>是否成功</returns>
public abstract bool Start(Server server, Mode mode);
}
}

View File

@@ -1,32 +0,0 @@
using System.Diagnostics;
using Netch.Models;
namespace Netch.Controllers
{
public abstract class ServerClient : Controller
{
/// <summary>
/// 启动
/// </summary>
/// <param name="server">服务器</param>
/// <param name="mode">模式</param>
/// <returns>是否启动成功</returns>
public abstract bool Start(Server server, Mode mode);
/// <summary>
/// ServerClient 停止
/// <param />
/// 注意 对象类型 以调用子类 Stop() 方法
/// </summary>
public new void Stop()
{
base.Stop();
// SSController Stop()
// 不能自动转换对象类型的兼容代码 :(
if (Global.Settings.BootShadowsocksFromDLL) NativeMethods.Shadowsocks.Stop();
}
public abstract void OnOutputDataReceived(object sender, DataReceivedEventArgs e);
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Netch.Forms;
@@ -12,29 +11,15 @@ namespace Netch.Controllers
{
public class MainController
{
/// <summary>
/// HTTP 控制器
/// </summary>
public HTTPController pHTTPController;
public EncryptedProxy pEncryptedProxyController;
/// <summary>
/// NF 控制器
/// </summary>
public NFController pNFController;
public ModeController pModeController;
/// <summary>
/// NTT 控制器
/// </summary>
public NTTController pNTTController;
public ServerClient pServerClientController;
/// <summary>
/// TUN/TAP 控制器
/// </summary>
public TUNTAPController pTUNTAPController;
[DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]
public static extern uint FlushDNSResolverCache();
@@ -46,6 +31,7 @@ namespace Netch.Controllers
/// <returns>是否启动成功</returns>
public bool Start(Server server, Mode mode)
{
pNTTController ??= new NTTController();
FlushDNSResolverCache();
var result = false;
@@ -58,61 +44,60 @@ namespace Netch.Controllers
switch (server.Type)
{
case "SS":
pServerClientController = new SSController();
pEncryptedProxyController = new SSController();
break;
case "SSR":
pServerClientController = new SSRController();
pEncryptedProxyController = new SSRController();
break;
case "VMess":
pServerClientController = new VMessController();
pEncryptedProxyController = new VMessController();
break;
case "Trojan":
pServerClientController = new TrojanController();
pEncryptedProxyController = new TrojanController();
break;
}
MainForm.Instance.StatusText(i18N.Translate("Starting ", pServerClientController.MainName));
if (pServerClientController.ready) result = pServerClientController.Start(server, mode);
MainForm.Instance.StatusText(i18N.Translate("Starting ", pEncryptedProxyController.AkaName));
if (pEncryptedProxyController.Ready) result = pEncryptedProxyController.Start(server, mode);
}
if (result) // If server runs,then run mode
if (result)
{
// 加密代理已启动
switch (mode.Type)
{
case 0: // 进程代理模式
pNFController ??= new NFController();
pNTTController ??= new NTTController();
if (pNFController.ready) result = pNFController.Start(server, mode, false);
if (!result && pNFController.ready)
{
MainForm.Instance.StatusText(i18N.Translate("Restarting Redirector"));
Logging.Info("正常启动失败后尝试停止驱动服务再重新启动");
//正常启动失败后尝试停止驱动服务再重新启动
result = pNFController.Start(server, mode, true);
}
if (result)
Task.Run(() => pNTTController.Start());
pModeController = new NFController();
break;
case 1: // TUN/TAP 黑名单代理模式
case 2: // TUN/TAP 白名单代理模式
pTUNTAPController ??= new TUNTAPController();
pNTTController ??= new NTTController();
result = pTUNTAPController.Start(server, mode);
pModeController = new TUNTAPController();
break;
case 3:
case 5:
pModeController = new HTTPController();
break;
case 4: // Socks5 代理模式不需要启动额外的Server
result = true;
break;
}
if (pModeController != null && pModeController.Ready)
{
MainForm.Instance.StatusText(i18N.Translate("Starting ", pModeController.AkaName));
result = pModeController.Start(server, mode);
}
switch (mode.Type)
{
case 0:
case 1:
case 2:
if (result)
Task.Run(() => pNTTController.Start());
break;
case 3: // HTTP 系统代理和 Socks5 和 HTTP 代理模式
case 5:
pHTTPController ??= new HTTPController();
result = pHTTPController.Start(server, mode);
break;
case 4: // Socks5 代理模式不需要启动额外的Server
break;
default:
result = false;
break;
}
}
if (!result) Stop();
@@ -124,36 +109,8 @@ namespace Netch.Controllers
/// </summary>
public void Stop()
{
pServerClientController?.Stop();
if (pNFController != null)
pNFController.Stop();
else if (pTUNTAPController != null)
pTUNTAPController.Stop();
else
pHTTPController?.Stop();
pNTTController?.Stop();
}
public static Process GetProcess(string path = null)
{
var p = new Process
{
StartInfo =
{
Arguments = "",
WorkingDirectory = $"{Global.NetchDir}\\bin",
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false
},
EnableRaisingEvents = true
};
if (path != null) p.StartInfo.FileName = Path.GetFullPath(path);
return p;
pEncryptedProxyController?.Stop();
pModeController?.Stop();
}
public static void KillProcessByName(string name)
@@ -166,11 +123,11 @@ namespace Netch.Controllers
}
catch (Win32Exception e)
{
Logging.Info($"结束进程 {name} 错误: " + e.Message);
Logging.Error($"结束进程 {name} 错误\n" + e);
}
catch (Exception)
{
// ignore
// ignored
}
}
}

View File

@@ -7,7 +7,7 @@ using Netch.Utils;
namespace Netch.Controllers
{
public class HTTPController
public class HTTPController : ModeController
{
/// <summary>
/// 实例
@@ -17,14 +17,22 @@ namespace Netch.Controllers
private string prevBypass, prevHTTP, prevPAC;
private bool prevEnabled;
public HTTPController()
{
AkaName = "HTTP";
Ready = true;
}
/// <summary>
/// 启动
/// </summary>
/// <param name="server">服务器</param>
/// <param name="mode">模式</param>
/// <returns>是否启动成功</returns>
public bool Start(Server server, Mode mode)
public override bool Start(Server server, Mode mode)
{
if (!Ready) return false;
RecordPrevious();
try
{
@@ -45,7 +53,7 @@ namespace Netch.Controllers
{
if (MessageBoxX.Show(i18N.Translate("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?"), confirm: true) == DialogResult.OK) Process.Start("https://netch.org/#/?id=%e4%be%9d%e8%b5%96");
Logging.Info("设置系统代理失败" + e);
Logging.Error("设置系统代理失败" + e);
return false;
}
@@ -72,18 +80,11 @@ namespace Netch.Controllers
/// <summary>
/// 停止
/// </summary>
public void Stop()
public override void Stop()
{
try
{
try
{
pPrivoxyController.Stop();
}
catch (Exception e)
{
Logging.Info(e.ToString());
}
pPrivoxyController.Stop();
NativeMethods.SetGlobal(prevHTTP, prevBypass);
if (prevPAC != "")
@@ -94,7 +95,7 @@ namespace Netch.Controllers
}
catch (Exception e)
{
Logging.Info(e.ToString());
Logging.Error("停止HTTP控制器错误\n" + e);
}
}
}

View File

@@ -0,0 +1,269 @@
using System;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
using Netch.Forms;
using Netch.Models;
using Netch.Utils;
using nfapinet;
namespace Netch.Controllers
{
public class NFController : ModeController
{
/// <summary>
/// 流量变动处理器
/// </summary>
/// <param name="upload">上传</param>
/// <param name="download">下载</param>
public delegate void BandwidthUpdateHandler(long upload, long download);
private readonly string _binDriverPath;
private readonly string _driverPath = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
private readonly ServiceController _service = new ServiceController("netfilter2");
private string _systemDriverVersion;
public NFController()
{
MainFile = "Redirector";
InitCheck();
// 驱动版本
_systemDriverVersion = FileVersionInfo.GetVersionInfo(_driverPath).FileVersion;
// 生成系统版本
var winNTver = $"{Environment.OSVersion.Version.Major.ToString()}.{Environment.OSVersion.Version.Minor.ToString()}";
var driverName = "";
switch (winNTver)
{
case "10.0":
driverName = "Win-10.sys";
break;
case "6.3":
case "6.2":
driverName = "Win-8.sys";
break;
case "6.1":
case "6.0":
driverName = "Win-7.sys";
break;
default:
Logging.Error($"不支持的系统版本:{winNTver}");
Ready = false;
return;
}
_binDriverPath = "bin\\" + driverName;
}
/// <summary>
/// 流量变动事件
/// </summary>
public event BandwidthUpdateHandler OnBandwidthUpdated;
public override bool Start(Server server, Mode mode)
{
if (!CheckDriverReady())
{
if (File.Exists(_driverPath))
UninstallDriver();
if (!InstallDriver())
return false;
}
var processList = "";
foreach (var proc in mode.Rule)
processList += proc + ",";
processList += "NTT.exe";
Instance = GetProcess("bin\\Redirector.exe");
if (server.Type != "Socks5")
{
Instance.StartInfo.Arguments += $"-r 127.0.0.1:{Global.Settings.Socks5LocalPort} -p \"{processList}\"";
}
else
{
var result = DNS.Lookup(server.Hostname);
if (result == null)
{
Logging.Info("无法解析服务器 IP 地址");
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}\"";
}
Instance.StartInfo.Arguments += $" -t {Global.Settings.RedirectorTCPPort}";
Instance.OutputDataReceived += OnOutputDataReceived;
Instance.ErrorDataReceived += OnOutputDataReceived;
for (var i = 0; i < 2; i++)
{
State = State.Starting;
Instance.Start();
Instance.BeginOutputReadLine();
Instance.BeginErrorReadLine();
for (var j = 0; j < 40; j++)
{
Thread.Sleep(250);
if (State == State.Started) return true;
}
Logging.Error("NF 进程启动超时");
Stop();
if (!RestartService()) return false;
}
return false;
}
private bool RestartService()
{
try
{
switch (_service.Status)
{
// 启动驱动服务
case ServiceControllerStatus.Running:
// 防止其他程序占用 重置 NF 百万连接数限制
_service.Stop();
_service.WaitForStatus(ServiceControllerStatus.Stopped);
MainForm.Instance.StatusText(i18N.Translate("Starting netfilter2 Service"));
_service.Start();
break;
case ServiceControllerStatus.Stopped:
MainForm.Instance.StatusText(i18N.Translate("Starting netfilter2 Service"));
_service.Start();
break;
}
}
catch (Exception e)
{
Logging.Error("启动驱动服务失败:\n" + e);
var result = NFAPI.nf_registerDriver("netfilter2");
if (result != NF_STATUS.NF_STATUS_SUCCESS)
{
Logging.Error($"注册驱动失败,返回值:{result}");
return false;
}
Logging.Info("注册驱动成功");
}
return true;
}
private bool CheckDriverReady()
{
// 检查驱动是否存在
if (!File.Exists(_driverPath)) return false;
// 检查驱动版本号
var binVersion = FileVersionInfo.GetVersionInfo(_binDriverPath).FileVersion;
return _systemDriverVersion.Equals(binVersion);
}
public bool UninstallDriver()
{
try
{
var service = new ServiceController("netfilter2");
if (service.Status == ServiceControllerStatus.Running)
{
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped);
}
}
catch (Exception)
{
// ignored
}
if (!File.Exists(_driverPath)) return true;
try
{
NFAPI.nf_unRegisterDriver("netfilter2");
File.Delete(_driverPath);
_systemDriverVersion = "";
return true;
}
catch (Exception ex)
{
throw ex;
}
}
public bool InstallDriver()
{
if (!Ready) return false;
Logging.Info("安装驱动中");
try
{
File.Copy(_binDriverPath, _driverPath);
}
catch (Exception e)
{
Logging.Error("驱动复制失败\n" + e);
return false;
}
MainForm.Instance.StatusText(i18N.Translate("Register driver"));
// 注册驱动文件
var result = NFAPI.nf_registerDriver("netfilter2");
if (result == NF_STATUS.NF_STATUS_SUCCESS)
{
_systemDriverVersion = FileVersionInfo.GetVersionInfo(_driverPath).FileVersion;
Logging.Info($"驱动安装成功,当前驱动版本:{_systemDriverVersion}");
}
else
{
Logging.Error($"注册驱动失败,返回值:{result}");
return false;
}
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()
{
StopInstance();
}
}
}

View File

@@ -1,9 +1,13 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Netch.Forms;
using Netch.Models;
using Netch.Properties;
@@ -11,32 +15,28 @@ using Netch.Utils;
namespace Netch.Controllers
{
public class TUNTAPController : Controller
public class TUNTAPController : ModeController
{
// ByPassLan IP
private readonly List<string> BypassLanIPs = new List<string> {"10.0.0.0/8", "172.16.0.0/16", "192.168.0.0/16"};
private readonly List<string> _bypassLanIPs = new List<string> {"10.0.0.0/8", "172.16.0.0/16", "192.168.0.0/16"};
private Mode _savedMode = new Mode();
private Server _savedServer = new Server();
/// <summary>
/// 服务器 IP 地址
/// </summary>
private IPAddress[] _serverAddresses = new IPAddress[0];
/// <summary>
/// 本地 DNS 服务控制器
/// </summary>
public DNSController pDNSController = new DNSController();
public Mode SavedMode = new Mode();
/// <summary>
/// 保存传入的规则
/// </summary>
public Server SavedServer = new Server();
/// <summary>
/// 服务器 IP 地址
/// </summary>
public IPAddress[] ServerAddresses = new IPAddress[0];
public TUNTAPController()
{
MainName = "tun2socks";
ready = BeforeStartProgress();
MainFile = "tun2socks";
InitCheck();
}
/// <summary>
@@ -45,16 +45,16 @@ namespace Netch.Controllers
public bool Configure()
{
// 查询服务器 IP 地址
var destination = Dns.GetHostAddressesAsync(SavedServer.Hostname);
var destination = Dns.GetHostAddressesAsync(_savedServer.Hostname);
if (destination.Wait(1000))
{
if (destination.Result.Length == 0) return false;
ServerAddresses = destination.Result;
_serverAddresses = destination.Result;
}
// 搜索出口
return Configuration.SearchOutbounds();
return SearchOutbounds();
}
/// <summary>
@@ -65,12 +65,12 @@ namespace Netch.Controllers
MainForm.Instance.StatusText(i18N.Translate("SetupBypass"));
Logging.Info("设置绕行规则 → 设置让服务器 IP 走直连");
// 让服务器 IP 走直连
foreach (var address in ServerAddresses)
foreach (var address in _serverAddresses)
if (!IPAddress.IsLoopback(address))
NativeMethods.CreateRoute(address.ToString(), 32, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
// 处理模式的绕过中国
if (SavedMode.BypassChina)
if (_savedMode.BypassChina)
{
Logging.Info("设置绕行规则 → 处理模式的绕过中国");
using (var sr = new StringReader(Encoding.UTF8.GetString(Resources.CNIP)))
@@ -98,7 +98,7 @@ namespace Netch.Controllers
Logging.Info("设置绕行规则 → 处理绕过局域网 IP");
// 处理绕过局域网 IP
foreach (var ip in BypassLanIPs)
foreach (var ip in _bypassLanIPs)
{
var info = ip.Split('/');
var address = IPAddress.Parse(info[0]);
@@ -106,7 +106,7 @@ namespace Netch.Controllers
if (!IPAddress.IsLoopback(address)) NativeMethods.CreateRoute(address.ToString(), int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
}
if (SavedMode.Type == 2) // 处理仅规则内走直连
if (_savedMode.Type == 2) // 处理仅规则内走直连
{
Logging.Info("设置绕行规则 → 处理仅规则内走直连");
// 将 TUN/TAP 网卡权重放到最高
@@ -129,14 +129,14 @@ namespace Netch.Controllers
{
State = State.Stopped;
foreach (var address in ServerAddresses) NativeMethods.DeleteRoute(address.ToString(), 32, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
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)
foreach (var ip in _savedMode.Rule)
{
var info = ip.Split('/');
@@ -145,10 +145,10 @@ namespace Netch.Controllers
NativeMethods.CreateRoute(info[0], prefix, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
}
}
else if (SavedMode.Type == 1) // 处理仅规则内走代理
else if (_savedMode.Type == 1) // 处理仅规则内走代理
{
Logging.Info("设置绕行规则->处理仅规则内走代理");
foreach (var ip in SavedMode.Rule)
foreach (var ip in _savedMode.Rule)
{
var info = ip.Split('/');
@@ -211,11 +211,11 @@ namespace Netch.Controllers
/// </summary>
public bool ClearBypass()
{
if (SavedMode.Type == 2)
if (_savedMode.Type == 2)
{
NativeMethods.DeleteRoute("0.0.0.0", 0, Global.Settings.TUNTAP.Gateway, Global.TUNTAP.Index, 10);
foreach (var ip in SavedMode.Rule)
foreach (var ip in _savedMode.Rule)
{
var info = ip.Split('/');
@@ -224,9 +224,9 @@ namespace Netch.Controllers
NativeMethods.DeleteRoute(info[0], prefix, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
}
}
else if (SavedMode.Type == 1)
else if (_savedMode.Type == 1)
{
foreach (var ip in SavedMode.Rule)
foreach (var ip in _savedMode.Rule)
{
var info = ip.Split('/');
@@ -278,7 +278,7 @@ namespace Netch.Controllers
if (!IPAddress.IsLoopback(address)) NativeMethods.DeleteRoute(address.ToString(), int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
}
foreach (var ip in BypassLanIPs)
foreach (var ip in _bypassLanIPs)
{
var info = ip.Split('/');
var address = IPAddress.Parse(info[0]);
@@ -286,7 +286,7 @@ namespace Netch.Controllers
if (!IPAddress.IsLoopback(address)) NativeMethods.DeleteRoute(address.ToString(), int.Parse(info[1]), Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
}
if (SavedMode.BypassChina)
if (_savedMode.BypassChina)
using (var sr = new StringReader(Encoding.UTF8.GetString(Resources.CNIP)))
{
string text;
@@ -299,24 +299,21 @@ namespace Netch.Controllers
}
}
foreach (var address in ServerAddresses)
foreach (var address in _serverAddresses)
if (!IPAddress.IsLoopback(address))
NativeMethods.DeleteRoute(address.ToString(), 32, Global.Adapter.Gateway.ToString(), Global.Adapter.Index);
return true;
}
/// <summary>
/// 启动
/// </summary>
/// <param name="server">配置</param>
/// <returns>是否成功</returns>
public bool Start(Server server, Mode mode)
public override bool Start(Server server, Mode mode)
{
if (!Ready) return false;
MainForm.Instance.StatusText(i18N.Translate("Starting Tap"));
SavedMode = mode;
SavedServer = server;
_savedMode = mode;
_savedServer = server;
if (!Configure()) return false;
@@ -324,7 +321,7 @@ namespace Netch.Controllers
SetupBypass();
Logging.Info("设置绕行规则完毕");
Instance = MainController.GetProcess("bin\\tun2socks.exe");
Instance = GetProcess("bin\\tun2socks.exe");
var adapterName = TUNTAP.GetName(Global.TUNTAP.ComponentID);
Logging.Info($"tun2sock使用适配器{adapterName}");
@@ -353,15 +350,13 @@ namespace Netch.Controllers
if (Global.Settings.TUNTAP.UseFakeDNS) dns += " -fakeDns";
if (server.Type == "Socks5")
Instance.StartInfo.Arguments = string.Format("-proxyServer {0}:{1} -tunAddr {2} -tunMask {3} -tunGw {4} -tunDns {5} -tunName \"{6}\"", server.Hostname, server.Port, Global.Settings.TUNTAP.Address, Global.Settings.TUNTAP.Netmask, Global.Settings.TUNTAP.Gateway, dns, adapterName);
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}\"";
else
Instance.StartInfo.Arguments = string.Format("-proxyServer 127.0.0.1:{0} -tunAddr {1} -tunMask {2} -tunGw {3} -tunDns {4} -tunName \"{5}\"", Global.Settings.Socks5LocalPort, Global.Settings.TUNTAP.Address, Global.Settings.TUNTAP.Netmask, Global.Settings.TUNTAP.Gateway, dns, adapterName);
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}\"";
Instance.ErrorDataReceived += OnOutputDataReceived;
Instance.OutputDataReceived += OnOutputDataReceived;
Logging.Info(Instance.StartInfo.Arguments);
State = State.Starting;
Instance.Start();
Instance.BeginErrorReadLine();
@@ -387,14 +382,14 @@ namespace Netch.Controllers
/// <summary>
/// TUN/TAP停止
/// </summary>
public new void Stop()
public override void Stop()
{
base.Stop();
StopInstance();
ClearBypass();
pDNSController.Stop();
}
public void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!WriteLog(e)) return;
if (State == State.Starting)
@@ -404,5 +399,113 @@ namespace Netch.Controllers
else if (e.Data.Contains("failed") || e.Data.Contains("invalid vconfig file")) State = State.Stopped;
}
}
/// <summary>
/// 搜索出口
/// </summary>
public static bool SearchOutbounds()
{
Logging.Info("正在搜索出口中");
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 搜索失败");
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;
}
// 搜索 TUN/TAP 适配器的索引
Global.TUNTAP.ComponentID = TUNTAP.GetComponentID();
if (string.IsNullOrEmpty(Global.TUNTAP.ComponentID))
{
Logging.Error("未找到可用 TUN/TAP 适配器");
if (MessageBoxX.Show(i18N.Translate("TUN/TAP driver is not detected. Is it installed now?"), confirm: true) == DialogResult.OK)
{
Configuration.addtap();
//给点时间不然立马安装完毕就查找适配器可能会导致找不到适配器ID
Thread.Sleep(1000);
Global.TUNTAP.ComponentID = TUNTAP.GetComponentID();
}
else
{
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)
{
Global.TUNTAP.Adapter = adapter;
Global.TUNTAP.Index = adapter.GetIPProperties().GetIPv4Properties().Index;
Logging.Info($"找到适配器TUN/TAP{adapter.Id}");
return true;
}
Logging.Error("无法找到出口");
return false;
}
}
}

View File

@@ -1,269 +0,0 @@
using System;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
using Netch.Forms;
using Netch.Models;
using Netch.Utils;
using nfapinet;
namespace Netch.Controllers
{
public class NFController : Controller
{
/// <summary>
/// 流量变动处理器
/// </summary>
/// <param name="upload">上传</param>
/// <param name="download">下载</param>
public delegate void BandwidthUpdateHandler(long upload, long download);
/// 驱动文件路径
private readonly string _driverPath = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
public NFController()
{
MainName = "Redirector";
ready = BeforeStartProgress();
}
/// <summary>
/// 流量变动事件
/// </summary>
public event BandwidthUpdateHandler OnBandwidthUpdated;
/// <summary>
/// 启动
/// </summary>
/// <param name="server">服务器</param>
/// <param name="mode">模式</param>
/// <param name="StopServiceAndRestart">先停止驱动服务再重新启动</param>
/// <returns>是否成功</returns>
public bool Start(Server server, Mode mode, bool StopServiceAndRestart)
{
if (!StopServiceAndRestart)
MainForm.Instance.StatusText(i18N.Translate("Starting Redirector"));
// 检查驱动是否存在
if (File.Exists(_driverPath))
{
// 生成系统版本
var version = $"{Environment.OSVersion.Version.Major.ToString()}.{Environment.OSVersion.Version.Minor.ToString()}";
var driverName = "";
switch (version)
{
case "10.0":
driverName = "Win-10.sys";
break;
case "6.3":
case "6.2":
driverName = "Win-8.sys";
break;
case "6.1":
case "6.0":
driverName = "Win-7.sys";
break;
default:
Logging.Info($"不支持的系统版本:{version}");
return false;
}
// 检查驱动版本号
var SystemfileVerInfo = FileVersionInfo.GetVersionInfo(_driverPath);
var BinFileVerInfo = FileVersionInfo.GetVersionInfo(string.Format("bin\\{0}", driverName));
if (!SystemfileVerInfo.FileVersion.Equals(BinFileVerInfo.FileVersion))
{
Logging.Info("开始更新驱动");
// 需要更新驱动
try
{
var service = new ServiceController("netfilter2");
if (service.Status == ServiceControllerStatus.Running)
{
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped);
}
NFAPI.nf_unRegisterDriver("netfilter2");
//删除老驱动
File.Delete(_driverPath);
if (!InstallDriver())
return false;
Logging.Info($"驱动更新完毕,当前驱动版本:{BinFileVerInfo.FileVersion}");
}
catch (Exception)
{
Logging.Info("更新驱动出错");
}
}
}
else
{
if (!InstallDriver()) return false;
}
try
{
// 启动驱动服务
var service = new ServiceController("netfilter2");
if (service.Status == ServiceControllerStatus.Running && StopServiceAndRestart)
{
// 防止其他程序占用 重置 NF 百万连接数限制
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped);
MainForm.Instance.StatusText(i18N.Translate("Starting netfilter2 Service"));
service.Start();
}
else if (service.Status == ServiceControllerStatus.Stopped)
{
MainForm.Instance.StatusText(i18N.Translate("Starting netfilter2 Service"));
service.Start();
}
}
catch (Exception e)
{
Logging.Info(e.ToString());
var result = NFAPI.nf_registerDriver("netfilter2");
if (result != NF_STATUS.NF_STATUS_SUCCESS)
{
Logging.Info($"注册驱动失败,返回值:{result}");
return false;
}
}
var processes = "";
foreach (var proc in mode.Rule)
{
processes += proc;
processes += ",";
}
processes += "NTT.exe";
Instance = MainController.GetProcess("bin\\Redirector.exe");
if (server.Type != "Socks5")
{
Instance.StartInfo.Arguments += $"-r 127.0.0.1:{Global.Settings.Socks5LocalPort} -p \"{processes}\"";
}
else
{
var result = DNS.Lookup(server.Hostname);
if (result == null)
{
Logging.Info("无法解析服务器 IP 地址");
return false;
}
Instance.StartInfo.Arguments += $"-r {result}:{server.Port} -p \"{processes}\"";
if (!string.IsNullOrWhiteSpace(server.Username) && !string.IsNullOrWhiteSpace(server.Password)) Instance.StartInfo.Arguments += $" -username \"{server.Username}\" -password \"{server.Password}\"";
}
Instance.StartInfo.Arguments += $" -t {Global.Settings.RedirectorTCPPort}";
Logging.Info(Instance.StartInfo.Arguments);
Instance.OutputDataReceived += OnOutputDataReceived;
Instance.ErrorDataReceived += OnOutputDataReceived;
State = State.Starting;
Instance.Start();
Instance.BeginOutputReadLine();
Instance.BeginErrorReadLine();
for (var i = 0; i < 10; i++)
{
Thread.Sleep(1000);
if (State == State.Started) return true;
}
Logging.Info("NF 进程启动超时");
Stop();
return false;
}
public bool InstallDriver()
{
Logging.Info("安装驱动中");
// 生成系统版本
var version = $"{Environment.OSVersion.Version.Major.ToString()}.{Environment.OSVersion.Version.Minor.ToString()}";
// 检查系统版本并复制对应驱动
try
{
switch (version)
{
case "10.0":
File.Copy("bin\\Win-10.sys", _driverPath);
Logging.Info("已复制 Win10 驱动");
break;
case "6.3":
case "6.2":
File.Copy("bin\\Win-8.sys", _driverPath);
Logging.Info("已复制 Win8 驱动");
break;
case "6.1":
case "6.0":
File.Copy("bin\\Win-7.sys", _driverPath);
Logging.Info("已复制 Win7 驱动");
break;
default:
Logging.Info($"不支持的系统版本:{version}");
return false;
}
}
catch (Exception e)
{
Logging.Info("复制驱动文件失败");
Logging.Info(e.ToString());
return false;
}
MainForm.Instance.StatusText(i18N.Translate("Register driver"));
// 注册驱动文件
var result = NFAPI.nf_registerDriver("netfilter2");
if (result != NF_STATUS.NF_STATUS_SUCCESS)
{
Logging.Info($"注册驱动失败,返回值:{result}");
return false;
}
return true;
}
public 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));
}
}
}
}
}
}

View File

@@ -12,21 +12,22 @@ namespace Netch.Controllers
{
public NTTController()
{
MainName = "NTT";
ready = BeforeStartProgress();
MainFile = "NTT";
InitCheck();
}
/// <summary>
/// 启动NatTypeTester
/// 启动 NatTypeTester
/// </summary>
/// <returns></returns>
public (bool, string, string, string) Start()
{
if (!Ready) return (false, null, null, null);
Thread.Sleep(1000);
MainForm.Instance.NatTypeStatusText(i18N.Translate("Starting NatTester"));
try
{
Instance = MainController.GetProcess("bin\\NTT.exe");
Instance = GetProcess("bin\\NTT.exe");
Instance.StartInfo.Arguments = $" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}";
@@ -39,7 +40,7 @@ namespace Netch.Controllers
Instance.BeginErrorReadLine();
Instance.WaitForExit();
var result = File.ReadAllText($"logging\\{MainName}.log").Split('#');
var result = File.ReadAllText($"logging\\{MainFile}.log").Split('#');
var natType = result[0];
var localEnd = result[1];
var publicEnd = result[2];
@@ -49,15 +50,22 @@ namespace Netch.Controllers
}
catch (Exception)
{
Logging.Info("NTT 进程出错");
Logging.Error("NTT 进程出错");
Stop();
return (false, null, null, null);
}
}
public void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
{
WriteLog(e);
}
/// <summary>
/// 无用
/// </summary>
public override void Stop()
{
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Diagnostics;
using System;
using System.Diagnostics;
using System.IO;
using Netch.Models;
@@ -8,34 +9,44 @@ namespace Netch.Controllers
{
public PrivoxyController()
{
MainName = "Privoxy";
MainFile = "Privoxy";
ExtFiles = new[] {"default.conf"};
ready = BeforeStartProgress();
InitCheck();
}
public bool Start(Server server, Mode mode)
{
if (server.Type != "Socks5")
File.WriteAllText("data\\privoxy.conf", File.ReadAllText("bin\\default.conf").Replace("_BIND_PORT_", Global.Settings.HTTPLocalPort.ToString()).Replace("_DEST_PORT_", Global.Settings.Socks5LocalPort.ToString()).Replace("0.0.0.0", Global.Settings.LocalAddress));
else
File.WriteAllText("data\\privoxy.conf", File.ReadAllText("bin\\default.conf").Replace("_BIND_PORT_", Global.Settings.HTTPLocalPort.ToString()).Replace("_DEST_PORT_", server.Port.ToString()).Replace("s 0.0.0.0", $"s {Global.Settings.LocalAddress}").Replace("/ 127.0.0.1", $"/ {server.Hostname}"));
if (!Ready) return false;
var isSocks5 = server.Type == "Socks5";
var socks5Port = isSocks5 ? server.Port : Global.Settings.Socks5LocalPort;
var text = File.ReadAllText("bin\\default.conf")
.Replace("_BIND_PORT_", Global.Settings.HTTPLocalPort.ToString())
.Replace("_DEST_PORT_", socks5Port.ToString())
.Replace("0.0.0.0", Global.Settings.LocalAddress);
if (isSocks5)
text = text.Replace("/ 127.0.0.1", $"/ {server.Hostname}");
File.WriteAllText("data\\privoxy.conf", text);
Instance = new Process
Instance = GetProcess("bin\\Privoxy.exe", false);
Instance.StartInfo.Arguments = "..\\data\\privoxy.conf";
Instance.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
Instance.StartInfo.UseShellExecute = true;
try
{
StartInfo =
{
FileName = $"{Global.NetchDir}\\bin\\Privoxy.exe",
Arguments = "..\\data\\privoxy.conf",
WorkingDirectory = $"{Global.NetchDir}\\bin",
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
CreateNoWindow = true
}
};
Instance.Start();
Instance.Start();
}
catch (Exception)
{
return false;
}
return true;
}
public override void Stop()
{
StopInstance();
}
}
}

View File

@@ -36,7 +36,7 @@ namespace Netch.Forms
//MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ControlButton.Enabled = SettingsButton.Enabled = false;
UpdateStatus(State.Starting);
Firewall.AddNetchFwRules();
Task.Run(() =>
@@ -44,7 +44,7 @@ namespace Netch.Forms
var server = ServerComboBox.SelectedItem as Models.Server;
var mode = ModeComboBox.SelectedItem as Models.Mode;
MainController = new MainController();
MainController ??= new MainController();
var startResult = MainController.Start(server, mode);
@@ -94,7 +94,7 @@ namespace Netch.Forms
if (server.Type == "Socks5")
{
// 不可控Socks5
if (mode.Type == 3 && mode.Type == 5)
if (mode.Type == 3 || mode.Type == 5)
{
// 可控HTTP
text.Append(

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.ServiceProcess;
@@ -18,7 +17,9 @@ using WebClient = Netch.Override.WebClient;
namespace Netch.Forms
{
partial class Dummy
{}
{
}
partial class MainForm
{
#region MenuStrip
@@ -160,7 +161,7 @@ namespace Netch.Forms
}
catch (Exception)
{
// 跳过
// ignored
}
Global.Settings.Server = Global.Settings.Server.Where(server => server.Group != item.Remark).ToList();
@@ -261,39 +262,18 @@ namespace Netch.Forms
Task.Run(() =>
{
var driver = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
if (File.Exists(driver))
try
{
try
if (new NFController().UninstallDriver())
{
var service = new ServiceController("netfilter2");
if (service.Status == ServiceControllerStatus.Running)
{
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped);
}
}
catch (Exception)
{
// 跳过
}
try
{
NFAPI.nf_unRegisterDriver("netfilter2");
File.Delete(driver);
MessageBoxX.Show(i18N.Translate("Service has been uninstalled"), owner: this);
}
catch (Exception ex)
{
MessageBoxX.Show(i18N.Translate("Error") + i18N.Translate(": ") + ex, info: false, owner: this);
}
}
else
catch (Exception e)
{
MessageBoxX.Show(i18N.Translate("Service has been uninstalled"), owner: this);
MessageBox.Show(i18N.Translate("Error", e.Message));
Console.WriteLine(e);
throw;
}
Enabled = true;
@@ -398,8 +378,8 @@ namespace Netch.Forms
}
catch (Exception e)
{
Logging.Info("使用代理更新 ACL 失败!" + e.Message);
MessageBoxX.Show(i18N.Translate("ACL update failed") + "\n" + e.Message);
Logging.Error("使用代理更新 ACL 失败!" + e);
MessageBoxX.Show(i18N.Translate("ACL update failed") + "\n" + e);
}
finally
{
@@ -414,17 +394,7 @@ namespace Netch.Forms
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
if (State != State.Waiting && State != State.Stopped)
{
ControlButton_Click(sender, e);
}
SaveConfigs();
UpdateStatus(State.Terminating);
NotifyIcon.Visible = false;
Close();
Dispose();
Environment.Exit(Environment.ExitCode);
Exit(true);
}
private void RelyToolStripMenuItem_Click(object sender, EventArgs e)
@@ -461,7 +431,7 @@ namespace Netch.Forms
}
else
{
Logging.Info("ACL 更新失败!" + args.Error);
Logging.Error("ACL 更新失败!" + args.Error);
MessageBoxX.Show(i18N.Translate("ACL update failed") + "\n" + args.Error);
}
}

View File

@@ -13,7 +13,7 @@ namespace Netch.Forms
partial class MainForm
{
// init at MainFrom_Load()
/// init at <see cref="MainForm_Load"/>
private int _sizeHeight;
private int _controlHeight;
private int _profileBoxHeight;

View File

@@ -148,7 +148,7 @@ namespace Netch.Forms
// 如果勾选了关闭时退出,自动点击退出按钮
else
{
ExitToolStripButton.PerformClick();
Exit(true);
}
}
}
@@ -414,35 +414,52 @@ namespace Netch.Forms
Activate();
}
private void ExitToolStripButton_Click(object sender, EventArgs e)
private void Exit(bool forceExit = false)
{
// 已启动
if (State != State.Waiting && State != State.Stopped)
{
// 未开启自动停止
if (!Global.Settings.StopWhenExited)
if (forceExit)
ControlFun();
else
{
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
if (!Global.Settings.StopWhenExited)
{
// 未开启自动停止
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
Visible = true;
ShowInTaskbar = true; // 显示在系统任务栏
WindowState = FormWindowState.Normal; // 还原窗体
NotifyIcon.Visible = true; // 托盘图标隐藏
Visible = true;
ShowInTaskbar = true; // 显示在系统任务栏
WindowState = FormWindowState.Normal; // 还原窗体
NotifyIcon.Visible = true; // 托盘图标隐藏
return;
return;
}
}
// 自动停止
ControlButton_Click(sender, e);
}
SaveConfigs();
UpdateStatus(State.Terminating);
NotifyIcon.Visible = false;
Close();
Dispose();
Environment.Exit(Environment.ExitCode);
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();
}
private void NotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)

View File

@@ -4,7 +4,7 @@
{
protected override void OnRenderToolStripBorder(System.Windows.Forms.ToolStripRenderEventArgs e)
{
// 跳过
// ignored
}
}
}

View File

@@ -15,13 +15,13 @@ namespace Netch.Utils
public static int received;
/// <summary>
/// 计算流量
/// </summary>
/// <param name="bandwidth">流量</param>
/// <returns>带单位的流量字符串</returns>
public static string Compute(long bandwidth)
/// 计算流量
/// </summary>
/// <param name="bandwidth">流量</param>
/// <returns>带单位的流量字符串</returns>
public static string Compute(long bandwidth)
{
string[] units = { "KB", "MB", "GB", "TB", "PB" };
string[] units = {"KB", "MB", "GB", "TB", "PB"};
double result = bandwidth;
var i = -1;
@@ -50,27 +50,24 @@ namespace Netch.Utils
//var processList = Process.GetProcessesByName(ProcessName).Select(p => p.Id).ToHashSet();
var processList = new List<int>();
if (server.Type.Equals("Socks5") && mainController.pHTTPController != null)
if (server.Type.Equals("Socks5") && mainController.pModeController.AkaName == "HTTP")
{
processList.Add(mainController.pHTTPController.pPrivoxyController.Instance.Id);
processList.Add(((HTTPController) mainController.pModeController).pPrivoxyController.Instance.Id);
}
else if (server.Type.Equals("SS") && Global.Settings.BootShadowsocksFromDLL)
{
processList.Add(Process.GetCurrentProcess().Id);
}
else if (mainController.pServerClientController != null)
else if (mainController.pEncryptedProxyController != null)
{
// mainController.pServerClientController.Instance
processList.Add(mainController.pServerClientController.Instance.Id);
processList.Add(mainController.pEncryptedProxyController.Instance.Id);
}
else if (mainController.pTUNTAPController != null)
else if (mainController.pModeController != null)
{
processList.Add(mainController.pTUNTAPController.Instance.Id);
}
else if (mainController.pNFController != null)
{
processList.Add(mainController.pNFController.Instance.Id);
processList.Add(mainController.pModeController.Instance.Id);
}
Logging.Info("启动流量统计 PID" + string.Join(",", processList.ToArray()));
Task.Run(() =>
@@ -109,6 +106,7 @@ namespace Netch.Utils
MainForm.Instance.OnBandwidthUpdated(0);
received = 0;
}
while (MainForm.Instance.State != State.Stopped)
{
Task.Delay(1000).Wait();
@@ -117,7 +115,6 @@ namespace Netch.Utils
MainForm.Instance.OnBandwidthUpdated(Convert.ToInt64(received));
}
}
}
}
}
}

View File

@@ -89,112 +89,6 @@ namespace Netch.Utils
File.WriteAllText(SETTINGS_JSON, JsonConvert.SerializeObject(Global.Settings, Formatting.Indented));
}
/// <summary>
/// 搜索出口
/// </summary>
public static bool SearchOutbounds()
{
Logging.Info("正在搜索出口中");
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.Info("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.Info("出口无 IPv4 地址,当前只支持 IPv4 地址");
return false;
}
break;
}
}
catch (Exception)
{ }
}
if (!AddressGot)
{
Logging.Info("无法找到当前使用适配器");
return false;
}
// 搜索 TUN/TAP 适配器的索引
Global.TUNTAP.ComponentID = TUNTAP.GetComponentID();
if (string.IsNullOrEmpty(Global.TUNTAP.ComponentID))
{
Logging.Info("未找到可用 TUN/TAP 适配器");
if (MessageBoxX.Show(i18N.Translate("TUN/TAP driver is not detected. Is it installed now?"),confirm:true) == DialogResult.OK)
{
addtap();
//给点时间不然立马安装完毕就查找适配器可能会导致找不到适配器ID
Thread.Sleep(1000);
Global.TUNTAP.ComponentID = TUNTAP.GetComponentID();
}
else
{
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)
{
Global.TUNTAP.Adapter = adapter;
Global.TUNTAP.Index = adapter.GetIPProperties().GetIPv4Properties().Index;
Logging.Info($"找到适配器:{adapter.Id}");
return true;
}
}
Logging.Info("无法找到出口");
return false;
}
/// <summary>
/// 安装tap网卡
/// </summary>

View File

@@ -1,9 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using NetFwTypeLib;
namespace Netch.Utils
@@ -24,8 +20,8 @@ namespace Netch.Utils
"Netch.exe"
};
private const string _netch = "Netch";
private const string _netchAutoRule = "NetchAutoRule";
private const string Netch = "Netch";
private const string NetchAutoRule = "NetchAutoRule";
/// <summary>
/// 添加防火墙规则 (非 Netch 自带程序)
@@ -33,7 +29,7 @@ namespace Netch.Utils
/// <param name="exeFullPath"></param>
public static void AddFwRule(string exeFullPath)
{
AddFwRule(_netchAutoRule, exeFullPath);
AddFwRule(NetchAutoRule, exeFullPath);
}
/// <summary>
@@ -41,7 +37,7 @@ namespace Netch.Utils
/// </summary>
public static void RemoveFwRules()
{
RemoveFwRules(_netchAutoRule);
RemoveFwRules(NetchAutoRule);
}
/// <summary>
@@ -49,7 +45,7 @@ namespace Netch.Utils
/// </summary>
public static void AddNetchFwRules()
{
if (GetFwRulePath(_netch).StartsWith(Global.NetchDir) && GetFwRulesNumber(_netch) >= ProgramPath.Length) return;
if (GetFwRulePath(Netch).StartsWith(Global.NetchDir) && GetFwRulesNumber(Netch) >= ProgramPath.Length) return;
RemoveNetchFwRules();
foreach (var p in ProgramPath)
{
@@ -64,9 +60,9 @@ namespace Netch.Utils
/// <summary>
/// 清除防火墙规则 (Netch 自带程序)
/// </summary>
public static void RemoveNetchFwRules()
private static void RemoveNetchFwRules()
{
RemoveFwRules(_netch);
RemoveFwRules(Netch);
}
#region
@@ -111,7 +107,7 @@ namespace Netch.Utils
var rule = (INetFwRule2)FwPolicy.Rules.Item(ruleName);
return rule.ApplicationName;
}
catch (Exception e)
catch (Exception)
{
return "";
}
@@ -120,7 +116,13 @@ namespace Netch.Utils
private static int GetFwRulesNumber(string ruleName)
{
// https://stackoverflow.com/a/53601691
return FwPolicy.Rules.Cast<INetFwRule>().Count(rule => rule.Name == ruleName);
var i = 0;
foreach (INetFwRule2 rule in FwPolicy.Rules)
{
if (rule.Name == ruleName)
i++;
}
return i;
}
#endregion

View File

@@ -12,7 +12,16 @@ namespace Netch.Utils
/// <param name="text">内容</param>
public static void Info(string text)
{
File.AppendAllText("logging\\application.log", $@"[{DateTime.Now}] {text}{Global.EOF}");
File.AppendAllText("logging\\application.log", $@"[{DateTime.Now}][INFO] {text}{Global.EOF}");
}
/// <summary>
/// 错误
/// </summary>
/// <param name="text">内容</param>
public static void Error(string text)
{
File.AppendAllText("logging\\application.log", $@"[{DateTime.Now}][ERROR] {text}{Global.EOF}");
}
}
}

View File

@@ -123,7 +123,7 @@ namespace Netch.Utils
}
catch (Exception e)
{
Logging.Info(e.ToString());
Logging.Error(e.ToString());
return null;
}
@@ -256,7 +256,7 @@ namespace Netch.Utils
if (!Global.EncryptMethods.SS.Contains(data.EncryptMethod))
{
Logging.Info(string.Format("不支持的 SS 加密方式:{0}", data.EncryptMethod));
Logging.Error($"不支持的 SS 加密方式:{data.EncryptMethod}");
return null;
}
@@ -313,14 +313,14 @@ namespace Netch.Utils
data.TransferProtocol = vmess.net;
if (!Global.TransferProtocols.Contains(data.TransferProtocol))
{
Logging.Info(string.Format("不支持的 VMess 传输协议:{0}", data.TransferProtocol));
Logging.Error($"不支持的 VMess 传输协议:{data.TransferProtocol}");
return null;
}
data.FakeType = vmess.type;
if (data.FakeType.Length != 0 && !Global.FakeTypes.Contains(data.FakeType))
{
Logging.Info(string.Format("不支持的 VMess 伪装类型:{0}", data.FakeType));
Logging.Error($"不支持的 VMess 伪装类型:{data.FakeType}");
return null;
}
@@ -337,7 +337,7 @@ namespace Netch.Utils
{
if (!Global.EncryptMethods.VMessQUIC.Contains(vmess.host))
{
Logging.Info(string.Format("不支持的 VMess QUIC 加密方式:{0}", vmess.host));
Logging.Error($"不支持的 VMess QUIC 加密方式:{vmess.host}");
return null;
}
@@ -393,43 +393,43 @@ namespace Netch.Utils
case "SS":
if (!Global.EncryptMethods.SS.Contains(NetchLink.EncryptMethod))
{
Logging.Info($"不支持的 SS 加密方式:{NetchLink.EncryptMethod}");
Logging.Error($"不支持的 SS 加密方式:{NetchLink.EncryptMethod}");
return null;
}
break;
case "SSR":
if (!Global.EncryptMethods.SSR.Contains(NetchLink.EncryptMethod))
{
Logging.Info($"不支持的 SSR 加密方式:{NetchLink.EncryptMethod}");
Logging.Error($"不支持的 SSR 加密方式:{NetchLink.EncryptMethod}");
return null;
}
if (!Global.Protocols.Contains(NetchLink.Protocol))
{
Logging.Info($"不支持的 SSR 协议:{NetchLink.Protocol}");
Logging.Error($"不支持的 SSR 协议:{NetchLink.Protocol}");
return null;
}
if (!Global.OBFSs.Contains(NetchLink.OBFS))
{
Logging.Info($"不支持的 SSR 混淆:{NetchLink.OBFS}");
Logging.Error($"不支持的 SSR 混淆:{NetchLink.OBFS}");
return null;
}
break;
case "VMess":
if (!Global.TransferProtocols.Contains(NetchLink.TransferProtocol))
{
Logging.Info($"不支持的 VMess 传输协议:{NetchLink.TransferProtocol}");
Logging.Error($"不支持的 VMess 传输协议:{NetchLink.TransferProtocol}");
return null;
}
if (!Global.FakeTypes.Contains(NetchLink.FakeType))
{
Logging.Info($"不支持的 VMess 伪装类型:{NetchLink.FakeType}");
Logging.Error($"不支持的 VMess 伪装类型:{NetchLink.FakeType}");
return null;
}
if (NetchLink.TransferProtocol == "quic")
{
if (!Global.EncryptMethods.VMessQUIC.Contains(NetchLink.QUICSecure))
{
Logging.Info($"不支持的 VMess QUIC 加密方式:{NetchLink.QUICSecure}");
Logging.Error($"不支持的 VMess QUIC 加密方式:{NetchLink.QUICSecure}");
return null;
}
}
@@ -492,7 +492,7 @@ namespace Netch.Utils
}
catch (Exception e)
{
Logging.Info(e.ToString());
Logging.Error(e.ToString());
return null;
}

View File

@@ -281,7 +281,7 @@ namespace Netch
}
catch (Exception)
{
// 跳过
// ignored
}
finally
{