using System; using System.IO; using System.Threading.Tasks; using Netch.Models; using Netch.Utils; namespace Netch.Controllers { public static class MainController { public static EncryptedProxy EncryptedProxyController { get; private set; } public static ModeController ModeController { get; private set; } public static Server SavedServer; public static Mode SavedMode; public static bool IsSocks5Server => SavedServer.Type == "Socks5"; public static string LocalAddress; public static int RedirectorTcpPort; public static int HttpPort; public static int Socks5Port; public static bool NttTested; private static readonly NTTController NTTController = new NTTController(); /// /// 启动 /// /// 服务器 /// 模式 /// 是否启动成功 public static async Task Start(Server server, Mode mode) { Logging.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}"); #region Record Settings HttpPort = Global.Settings.HTTPLocalPort; Socks5Port = server.Type != "Socks5" ? Global.Settings.Socks5LocalPort : server.Port; RedirectorTcpPort = Global.Settings.RedirectorTCPPort; LocalAddress = server.Type != "Socks5" ? Global.Settings.LocalAddress : "127.0.0.1"; SavedServer = server; SavedMode = mode; #endregion NativeMethods.FlushDNSResolverCache(); _ = Task.Run(Firewall.AddNetchFwRules); bool result; if (IsSocks5Server) { result = mode.Type != 4; } else { EncryptedProxyController = server.Type switch { "SS" => new SSController(), "SSR" => new SSRController(), "VMess" => new VMessController(), "Trojan" => new TrojanController(), _ => EncryptedProxyController }; Utils.Utils.KillProcessByName(EncryptedProxyController.MainFile); #region 检查端口是否被占用 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; } var portNotAvailable = false; if (!IsSocks5Server) { portNotAvailable |= PortCheckAndShowMessageBox(Socks5Port, "Socks5"); } switch (SavedMode.Type) { case 0: portNotAvailable |= PortCheckAndShowMessageBox(RedirectorTcpPort, "Redirector TCP"); break; case 3: case 5: portNotAvailable |= PortCheckAndShowMessageBox(HttpPort, "HTTP"); break; } if (portNotAvailable) { Logging.Error("主控制器启动失败: 端口被占用"); return false; } #endregion Global.MainForm.StatusText(i18N.Translate("Starting ", EncryptedProxyController.Name)); try { result = await Task.Run(() => EncryptedProxyController.Start(server, mode)); } catch (Exception e) { Logging.Error("加密代理启动失败,未处理异常: " + e); result = false; } } if (result) { // 加密代理成功启动 switch (mode.Type) { case 0: // 进程代理模式 ModeController = new NFController(); break; case 1: // TUN/TAP 黑名单代理模式 case 2: // TUN/TAP 白名单代理模式 ModeController = new TUNTAPController(); break; case 3: case 5: ModeController = new HTTPController(); break; case 4: // Socks5 代理模式,不需要启动额外的Server result = true; break; } if (ModeController != null) { Global.MainForm.StatusText(i18N.Translate("Starting ", ModeController.Name)); try { result = await Task.Run(() => ModeController.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; } } if (result) { // 成功启动 if (!IsSocks5Server) PortHelper.UsingPorts.Add(Socks5Port); switch (mode.Type) // 记录使用端口 { case 0: PortHelper.UsingPorts.Add(RedirectorTcpPort); break; case 3: case 5: PortHelper.UsingPorts.Add(HttpPort); break; } switch (mode.Type) { case 0: case 1: case 2: NatTest(); break; } } } if (!result) { Logging.Error("主控制器启动失败"); try { await Stop(); } catch { // ignored } } return result; } /// /// 停止 /// public static async Task Stop() { HttpPort = Socks5Port = RedirectorTcpPort = 0; LocalAddress = null; SavedMode = null; SavedServer = null; PortHelper.UsingPorts.Clear(); var tasks = new[] { Task.Run(() => EncryptedProxyController?.Stop()), Task.Run(() => ModeController?.Stop()), Task.Run(() => NTTController.Stop()) }; await Task.WhenAll(tasks); } /// /// 测试 NAT /// public static void NatTest() { NttTested = false; Task.Run(() => { Global.MainForm.NatTypeStatusText(i18N.Translate("Starting NatTester")); // Thread.Sleep(1000); var (nttResult, natType, localEnd, publicEnd) = NTTController.Start(); if (nttResult) { var country = Utils.Utils.GetCityCode(publicEnd); Global.MainForm.NatTypeStatusText(natType, country); } else Global.MainForm.NatTypeStatusText(natType); NttTested = true; }); } } }