using System; using System.IO; using System.Net; using System.Threading.Tasks; using Netch.Models; using Netch.Utils; using static Netch.Forms.MainForm; using static Netch.Utils.PortHelper; namespace Netch.Controllers { public static class MainController { public static IServerController ServerController { get; private set; } public static IModeController ModeController { get; private set; } 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}"); if (server.IsSocks5() && mode.Type == 4) { return false; } NativeMethods.FlushDNSResolverCache(); try { WebUtil.BestLocalEndPoint(new IPEndPoint(0x72727272, 53)); } catch (Exception) { MessageBoxX.Show("No internet connection"); return false; } if (Global.Settings.ResolveServerHostname && DNS.Lookup(server.Hostname) == null) { MessageBoxX.Show("Lookup Server hostname failed"); return false; } _ = Task.Run(Firewall.AddNetchFwRules); try { if (!await StartServer(server, mode)) { throw new StartFailedException(); } if (!await StartMode(server, mode)) { throw new StartFailedException(); } if (ModeController?.TestNatRequired ?? false) NatTest(); return true; } catch (Exception e) { switch (e) { case DllNotFoundException _: case FileNotFoundException _: MessageBoxX.Show(e.Message + "\n\n" + i18N.Translate("Missing File or runtime components"), owner: Global.MainForm); break; case StartFailedException _: case PortInUseException _: break; default: Logging.Error($"主控制器未处理异常: {e}"); break; } try { await Stop(); } catch { // ignored } return false; } } private static async Task StartServer(Server server, Mode mode) { if (server.IsSocks5()) { return true; } ServerController = ServerHelper.GetUtilByTypeName(server.Type).GetController(); if (ServerController is Guard instanceController) { Utils.Utils.KillProcessByName(instanceController.MainFile); } PortCheckAndShowMessageBox(Global.Settings.Socks5LocalPort, "Socks5"); Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ServerController.Name)); if (await Task.Run(() => ServerController.Start(server, mode))) { UsingPorts.Add(StatusPortInfoText.Socks5Port = Global.Settings.Socks5LocalPort); StatusPortInfoText.ShareLan = Global.Settings.LocalAddress == "0.0.0.0"; return true; } return false; } private static async Task StartMode(Server server, Mode mode) { var port = 0; switch (mode.Type) { case 0: ModeController = new NFController(); PortCheckAndShowMessageBox(port = Global.Settings.RedirectorTCPPort, "Redirector TCP"); break; case 1: case 2: ModeController = new TUNTAPController(); break; case 3: case 5: ModeController = new HTTPController(); PortCheckAndShowMessageBox(port = Global.Settings.HTTPLocalPort, "HTTP"); break; case 4: return true; default: Logging.Error("未知模式类型"); return false; } Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name)); if (await Task.Run(() => ModeController.Start(server, mode))) { switch (mode.Type) { case 3: case 5: StatusPortInfoText.HttpPort = port; break; } UsingPorts.Add(port); return true; } return false; } /// /// 停止 /// public static async Task Stop() { UsingPorts.Clear(); _ = Task.Run(() => NTTController.Stop()); var tasks = new[] { Task.Run(() => ServerController?.Stop()), Task.Run(() => ModeController?.Stop()), }; await Task.WhenAll(tasks); ModeController = null; ServerController = null; } /// /// 检查端口是否被占用, /// 被占用则弹窗提示, 确认后抛出异常 /// /// 检查的端口 /// 端口用途名称 /// /// private static void PortCheckAndShowMessageBox(int port, string portName, PortType portType = PortType.Both) { if (PortInUse(port, portType)) { MessageBoxX.Show(i18N.TranslateFormat("The {0} port is in use.", $"{portName} ({port})")); throw new PortInUseException(); } } /// /// 测试 NAT /// public static void NatTest() { NttTested = false; Task.Run(() => { Global.MainForm.NatTypeStatusText(i18N.Translate("Starting NatTester")); // Thread.Sleep(1000); var (result, localEnd, publicEnd) = NTTController.Start(); if (!string.IsNullOrEmpty(publicEnd)) { var country = Utils.Utils.GetCityCode(publicEnd); Global.MainForm.NatTypeStatusText(result, country); } else Global.MainForm.NatTypeStatusText(result ?? "Error"); NttTested = true; }); } } public class StartFailedException : Exception { } }