mirror of
https://github.com/bolucat/Archive.git
synced 2025-09-26 20:21:35 +08:00
196 lines
6.2 KiB
C#
196 lines
6.2 KiB
C#
using System.Diagnostics;
|
|
using Microsoft.VisualStudio.Threading;
|
|
using Netch.Interfaces;
|
|
using Netch.Models;
|
|
using Netch.Models.Modes;
|
|
using Netch.Servers;
|
|
using Netch.Services;
|
|
using Netch.Utils;
|
|
|
|
namespace Netch.Controllers;
|
|
|
|
public static class MainController
|
|
{
|
|
public static Socks5Server? Socks5Server { get; private set; }
|
|
|
|
public static Server? Server { get; private set; }
|
|
|
|
public static Mode? Mode { get; private set; }
|
|
|
|
public static IServerController? ServerController { get; private set; }
|
|
|
|
public static IModeController? ModeController { get; private set; }
|
|
|
|
private static readonly AsyncSemaphore Lock = new(1);
|
|
|
|
public static async Task StartAsync(Server server, Mode mode)
|
|
{
|
|
using var releaser = await Lock.EnterAsync();
|
|
|
|
Log.Information("Start MainController: {Server} {Mode}", $"{server.Type}", $"[{(int)mode.Type}]{mode.i18NRemark}");
|
|
|
|
if (await DnsUtils.LookupAsync(server.Hostname) == null)
|
|
throw new MessageException(i18N.Translate("Lookup Server hostname failed"));
|
|
|
|
// TODO Disable NAT Type Test setting
|
|
// cache STUN Server ip to prevent "Wrong STUN Server"
|
|
DnsUtils.LookupAsync(Global.Settings.STUN_Server).Forget();
|
|
|
|
Server = server;
|
|
Mode = mode;
|
|
|
|
await Task.WhenAll(Task.Run(NativeMethods.RefreshDNSCache), Task.Run(Firewall.AddNetchFwRules));
|
|
|
|
try
|
|
{
|
|
ModeController = ModeService.GetModeControllerByType(mode.Type, out var modePort, out var portName);
|
|
|
|
if (modePort != null)
|
|
TryReleaseTcpPort((ushort)modePort, portName);
|
|
|
|
if (Server is Socks5Server socks5 && (!socks5.Auth() || ModeController.Features.HasFlag(ModeFeature.SupportSocks5Auth)))
|
|
{
|
|
Socks5Server = socks5;
|
|
}
|
|
else
|
|
{
|
|
// Start Server Controller to get a local socks5 server
|
|
Log.Debug("Server Information: {Data}", $"{server.Type} {server.MaskedData()}");
|
|
|
|
ServerController = new V2rayController();
|
|
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ServerController.Name));
|
|
|
|
TryReleaseTcpPort(ServerController.Socks5LocalPort(), "Socks5");
|
|
Socks5Server = await ServerController.StartAsync(server);
|
|
|
|
StatusPortInfoText.Socks5Port = Socks5Server.Port;
|
|
StatusPortInfoText.UpdateShareLan();
|
|
}
|
|
|
|
// Start Mode Controller
|
|
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
|
|
|
|
await ModeController.StartAsync(Socks5Server, mode);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
releaser.Dispose();
|
|
await StopAsync();
|
|
|
|
switch (e)
|
|
{
|
|
case DllNotFoundException:
|
|
case FileNotFoundException:
|
|
throw new Exception(e.Message + "\n\n" + i18N.Translate("Missing File or runtime components"));
|
|
case MessageException:
|
|
throw;
|
|
default:
|
|
Log.Error(e, "Unhandled Exception When Start MainController");
|
|
Utils.Utils.Open(Constants.LogFile);
|
|
throw new MessageException($"{i18N.Translate("Unhandled Exception")}\n{e.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public static async Task StopAsync()
|
|
{
|
|
if (Lock.CurrentCount == 0)
|
|
{
|
|
(await Lock.EnterAsync()).Dispose();
|
|
if (ServerController == null && ModeController == null)
|
|
// stopped
|
|
return;
|
|
|
|
// else begin stop
|
|
}
|
|
|
|
using var _ = await Lock.EnterAsync();
|
|
|
|
if (ServerController == null && ModeController == null)
|
|
return;
|
|
|
|
Log.Information("Stop Main Controller");
|
|
StatusPortInfoText.Reset();
|
|
|
|
var tasks = new[]
|
|
{
|
|
ServerController?.StopAsync() ?? Task.CompletedTask,
|
|
ModeController?.StopAsync() ?? Task.CompletedTask
|
|
};
|
|
|
|
try
|
|
{
|
|
await Task.WhenAll(tasks);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Error(e, "MainController Stop Error");
|
|
}
|
|
|
|
ServerController = null;
|
|
ModeController = null;
|
|
}
|
|
|
|
public static void PortCheck(ushort port, string portName, PortType portType = PortType.Both)
|
|
{
|
|
try
|
|
{
|
|
PortHelper.CheckPort(port, portType);
|
|
}
|
|
catch (PortInUseException)
|
|
{
|
|
throw new MessageException(i18N.TranslateFormat("The {0} port is in use.", $"{portName} ({port})"));
|
|
}
|
|
catch (PortReservedException)
|
|
{
|
|
throw new MessageException(i18N.TranslateFormat("The {0} port is reserved by system.", $"{portName} ({port})"));
|
|
}
|
|
}
|
|
|
|
public static void TryReleaseTcpPort(ushort port, string portName)
|
|
{
|
|
foreach (var p in PortHelper.GetProcessByUsedTcpPort(port))
|
|
{
|
|
var fileName = p.MainModule?.FileName;
|
|
if (fileName == null)
|
|
continue;
|
|
|
|
if (fileName.StartsWith(Global.NetchDir))
|
|
{
|
|
p.Kill();
|
|
p.WaitForExit();
|
|
}
|
|
else
|
|
{
|
|
throw new MessageException(i18N.TranslateFormat("The {0} port is used by {1}.", $"{portName} ({port})", $"({p.Id}){fileName}"));
|
|
}
|
|
}
|
|
|
|
PortCheck(port, portName, PortType.TCP);
|
|
}
|
|
|
|
public static Task<NatTypeTestResult> DiscoveryNatTypeAsync(CancellationToken ctx = default)
|
|
{
|
|
Debug.Assert(Socks5Server != null, nameof(Socks5Server) + " != null");
|
|
return Socks5ServerTestUtils.DiscoveryNatTypeAsync(Socks5Server, ctx);
|
|
}
|
|
|
|
public static Task<int?> HttpConnectAsync(CancellationToken ctx = default)
|
|
{
|
|
Debug.Assert(Socks5Server != null, nameof(Socks5Server) + " != null");
|
|
try
|
|
{
|
|
return Socks5ServerTestUtils.HttpConnectAsync(Socks5Server, ctx);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// ignored
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Warning(e, "Unhandled Socks5ServerTestUtils.HttpConnectAsync Exception");
|
|
}
|
|
|
|
return Task.FromResult<int?>(null);
|
|
}
|
|
} |