mirror of
https://github.com/netchx/netch.git
synced 2026-05-11 23:45:06 +08:00
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20d4682d40 | ||
|
|
6ddffcbca4 | ||
|
|
47faf6be88 | ||
|
|
71028fd6a9 | ||
|
|
a03991ddb9 | ||
|
|
414a43b719 | ||
|
|
32f8d15f86 | ||
|
|
5eb68359d3 | ||
|
|
958c28c695 | ||
|
|
320b1f66c9 | ||
|
|
7015784e4f | ||
|
|
64071721d8 | ||
|
|
e3b1ae0621 | ||
|
|
4836e4c913 | ||
|
|
a70b557e0c | ||
|
|
1b19c12824 | ||
|
|
35be8bedd0 | ||
|
|
5109134843 | ||
|
|
573b5179bb | ||
|
|
8c03f7c6db | ||
|
|
7d7c91bc68 | ||
|
|
5ed3f8e073 | ||
|
|
7636b0ed27 | ||
|
|
29b003bacd | ||
|
|
c0452552ec | ||
|
|
3aee365b48 | ||
|
|
58c5f9d086 | ||
|
|
62dc9166ce | ||
|
|
e7d04e36ac | ||
|
|
8357878b68 | ||
|
|
f5da527775 | ||
|
|
9f7fd9020f | ||
|
|
6f87280d57 | ||
|
|
76f7c987ac | ||
|
|
ff985b14a8 | ||
|
|
951cee1c06 | ||
|
|
d31617b65c | ||
|
|
80be24120b | ||
|
|
4676de32b9 | ||
|
|
1775b35513 | ||
|
|
80a92a401c | ||
|
|
b53ea1f7e4 | ||
|
|
486fa195e7 | ||
|
|
2d000f5f4f | ||
|
|
8a48e321b5 | ||
|
|
34acb6b281 | ||
|
|
5bbef8db12 | ||
|
|
4fba66fab8 | ||
|
|
6827207434 | ||
|
|
d8e60aa355 | ||
|
|
f3a7b7cf57 | ||
|
|
26fe7ad593 | ||
|
|
7493f07da9 | ||
|
|
f6dfb25e3f | ||
|
|
1e5d357f34 | ||
|
|
63f51a481c | ||
|
|
e89c742f3f | ||
|
|
458c6047af | ||
|
|
561def7fe1 | ||
|
|
9e68fb12fb | ||
|
|
d7360b3688 | ||
|
|
7844c183e7 | ||
|
|
8325bd1fe3 | ||
|
|
0a59d6aa3f | ||
|
|
7fa05e7dad | ||
|
|
b948040f9d | ||
|
|
0c76198bd4 | ||
|
|
d917e5a8fa | ||
|
|
015e4ada94 | ||
|
|
86b1741dd0 | ||
|
|
4af18025a7 | ||
|
|
3678c98fec | ||
|
|
9f809b4d27 | ||
|
|
e3a3396d18 | ||
|
|
43c19c6698 | ||
|
|
9ee2a2a31a | ||
|
|
489c3fc39d | ||
|
|
657266df47 | ||
|
|
82ed5189c8 | ||
|
|
ebe2978724 |
27
.github/dependabot.yml
vendored
27
.github/dependabot.yml
vendored
@@ -1,34 +1,21 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "07:00"
|
||||
time: "00:00"
|
||||
timezone: "Asia/Shanghai"
|
||||
labels:
|
||||
- "Automatic"
|
||||
open-pull-requests-limit: 99
|
||||
- "automatic"
|
||||
open-pull-requests-limit: 114514
|
||||
|
||||
- package-ecosystem: "nuget"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "07:15"
|
||||
time: "00:10"
|
||||
timezone: "Asia/Shanghai"
|
||||
labels:
|
||||
- "Automatic"
|
||||
open-pull-requests-limit: 99
|
||||
- package-ecosystem: "gitsubmodule"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "07:15"
|
||||
timezone: "Asia/Shanghai"
|
||||
labels:
|
||||
- "Automatic"
|
||||
open-pull-requests-limit: 99
|
||||
- "automatic"
|
||||
open-pull-requests-limit: 114514
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -41,6 +41,8 @@ jobs:
|
||||
Netch.7z
|
||||
body: |
|
||||
[](https://t.me/netch_channel) [](https://t.me/netch_group)
|
||||
## Changelogs
|
||||
* This is an automated deployment of GitHub Actions, the change log should be updated manually soon
|
||||
|
||||
## 更新日志
|
||||
* 这是 GitHub Actions 自动化部署,更新日志应该很快会手动更新
|
||||
|
||||
@@ -2,13 +2,24 @@
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public const string TempConfig = "data\\last.json";
|
||||
public const string TempRouteFile = "data\\route.txt";
|
||||
|
||||
public const string AioDnsRuleFile = "bin\\aiodns.conf";
|
||||
public const string NFDriver = "bin\\nfdriver.sys";
|
||||
public const string STUNServersFile = "bin\\stun.txt";
|
||||
|
||||
public const string LogFile = "logging\\application.log";
|
||||
|
||||
public const string OutputTemplate = @"[{Timestamp:yyyy-MM-dd HH:mm:ss}][{Level}] {Message:lj}{NewLine}{Exception}";
|
||||
public const string EOF = "\r\n";
|
||||
|
||||
public static class Parameter
|
||||
{
|
||||
public const string Show = "-show";
|
||||
public const string ForceUpdate = "-forceUpdate";
|
||||
public const string Console = "-console";
|
||||
}
|
||||
|
||||
public const string WintunDllFile = "bin\\wintun.dll";
|
||||
}
|
||||
}
|
||||
@@ -7,27 +7,28 @@ namespace Netch.Controllers
|
||||
{
|
||||
public class DNSController : IController
|
||||
{
|
||||
public string Name { get; } = "DNS Service";
|
||||
public string Name => "DNS Service";
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动DNS服务
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void Start()
|
||||
{
|
||||
MainController.PortCheck(Global.Settings.AioDNS.ListenPort, "DNS");
|
||||
|
||||
var aioDnsConfig = Global.Settings.AioDNS;
|
||||
var listenAddress = Global.Settings.LocalAddress;
|
||||
|
||||
Dial(NameList.TYPE_REST, "");
|
||||
Dial(NameList.TYPE_ADDR, $"{Global.Settings.LocalAddress}:{Global.Settings.AioDNS.ListenPort}");
|
||||
Dial(NameList.TYPE_LIST, Path.GetFullPath(Global.Settings.AioDNS.RulePath));
|
||||
Dial(NameList.TYPE_CDNS, $"{Global.Settings.AioDNS.ChinaDNS}");
|
||||
Dial(NameList.TYPE_ODNS, $"{Global.Settings.AioDNS.OtherDNS}");
|
||||
Dial(NameList.TYPE_ADDR, $"{listenAddress}:{aioDnsConfig.ListenPort}");
|
||||
Dial(NameList.TYPE_LIST, Path.GetFullPath(Constants.AioDnsRuleFile));
|
||||
Dial(NameList.TYPE_CDNS, $"{aioDnsConfig.ChinaDNS}");
|
||||
Dial(NameList.TYPE_ODNS, $"{aioDnsConfig.OtherDNS}");
|
||||
|
||||
if (!Init())
|
||||
throw new Exception("AioDNS start failed");
|
||||
throw new Exception("AioDNS start failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,3 @@
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
@@ -9,82 +6,157 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Timer = System.Timers.Timer;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public abstract class Guard
|
||||
{
|
||||
private readonly Timer _flushFileStreamTimer = new(300) { AutoReset = true };
|
||||
|
||||
private FileStream? _logFileStream;
|
||||
|
||||
private StreamWriter? _logStreamWriter;
|
||||
private bool _redirectToFile = true;
|
||||
|
||||
/// <summary>
|
||||
/// 日志文件(重定向输出文件)
|
||||
/// </summary>
|
||||
/// <param name="mainFile">application path relative of Netch\bin</param>
|
||||
/// <param name="redirectOutput"></param>
|
||||
/// <param name="encoding">application output encode</param>
|
||||
protected Guard(string mainFile, bool redirectOutput = true, Encoding? encoding = null)
|
||||
{
|
||||
RedirectOutput = redirectOutput;
|
||||
|
||||
var fileName = Path.GetFullPath($"bin\\{mainFile}");
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
throw new MessageException(i18N.Translate($"bin\\{mainFile} file not found!"));
|
||||
|
||||
Instance = new Process
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
FileName = fileName,
|
||||
WorkingDirectory = $"{Global.NetchDir}\\bin",
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = !RedirectOutput,
|
||||
RedirectStandardOutput = RedirectOutput,
|
||||
StandardOutputEncoding = RedirectOutput ? encoding : null,
|
||||
RedirectStandardError = RedirectOutput,
|
||||
StandardErrorEncoding = RedirectOutput ? encoding : null,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected string LogPath => Path.Combine(Global.NetchDir, $"logging\\{Name}.log");
|
||||
|
||||
/// <summary>
|
||||
/// 成功启动关键词
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<string> StartedKeywords { get; set; } = new List<string>();
|
||||
protected virtual IEnumerable<string> StartedKeywords { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 启动失败关键词
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<string> StoppedKeywords { get; set; } = new List<string>();
|
||||
protected virtual IEnumerable<string> FailedKeywords { get; } = new List<string>();
|
||||
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 主程序名
|
||||
/// </summary>
|
||||
public abstract string MainFile { get; protected set; }
|
||||
private State State { get; set; } = State.Waiting;
|
||||
|
||||
protected State State { get; set; } = State.Waiting;
|
||||
private bool RedirectOutput { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 进程是否可以重定向输出
|
||||
/// </summary>
|
||||
protected bool RedirectStd { get; set; } = true;
|
||||
public Process Instance { get; }
|
||||
|
||||
protected bool RedirectToFile
|
||||
~Guard()
|
||||
{
|
||||
get => RedirectStd && _redirectToFile;
|
||||
set => _redirectToFile = value;
|
||||
_logFileStream?.Dispose();
|
||||
_logStreamWriter?.Dispose();
|
||||
Instance.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 进程实例
|
||||
/// </summary>
|
||||
public Process? Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 程序输出的编码,
|
||||
/// </summary>
|
||||
protected virtual Encoding? InstanceOutputEncoding { get; } = null;
|
||||
|
||||
public abstract void Stop();
|
||||
|
||||
/// <summary>
|
||||
/// 停止进程
|
||||
/// </summary>
|
||||
protected void StopInstance()
|
||||
protected void StartGuard(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal)
|
||||
{
|
||||
State = State.Starting;
|
||||
|
||||
_logFileStream = File.Open(LogPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
|
||||
_logStreamWriter = new StreamWriter(_logFileStream) { AutoFlush = true };
|
||||
|
||||
Instance.StartInfo.Arguments = argument;
|
||||
Instance.Start();
|
||||
|
||||
if (priority != ProcessPriorityClass.Normal)
|
||||
Instance.PriorityClass = priority;
|
||||
|
||||
if (RedirectOutput)
|
||||
{
|
||||
Task.Run(() => ReadOutput(Instance.StandardOutput));
|
||||
Task.Run(() => ReadOutput(Instance.StandardError));
|
||||
|
||||
if (!StartedKeywords.Any())
|
||||
{
|
||||
// Skip, No started keyword
|
||||
State = State.Started;
|
||||
return;
|
||||
}
|
||||
|
||||
// wait ReadOutput change State
|
||||
for (var i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
switch (State)
|
||||
{
|
||||
case State.Started:
|
||||
OnStarted();
|
||||
return;
|
||||
case State.Stopped:
|
||||
StopGuard();
|
||||
OnStartFailed();
|
||||
throw new MessageException($"{Name} 控制器启动失败");
|
||||
}
|
||||
}
|
||||
|
||||
StopGuard();
|
||||
throw new MessageException($"{Name} 控制器启动超时");
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadOutput(TextReader reader)
|
||||
{
|
||||
string? line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
_logStreamWriter!.WriteLine(line);
|
||||
OnReadNewLine(line);
|
||||
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (StartedKeywords.Any(s => line.Contains(s)))
|
||||
State = State.Started;
|
||||
else if (FailedKeywords.Any(s => line.Contains(s)))
|
||||
{
|
||||
OnStartFailed();
|
||||
State = State.Stopped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
State = State.Stopped;
|
||||
}
|
||||
|
||||
public virtual void Stop()
|
||||
{
|
||||
StopGuard();
|
||||
}
|
||||
|
||||
protected void StopGuard()
|
||||
{
|
||||
_logStreamWriter?.Close();
|
||||
_logFileStream?.Close();
|
||||
|
||||
try
|
||||
{
|
||||
if (Instance == null || Instance.HasExited)
|
||||
return;
|
||||
|
||||
Instance.Kill();
|
||||
Instance.WaitForExit();
|
||||
if (Instance is { HasExited: false })
|
||||
{
|
||||
Instance.Kill();
|
||||
Instance.WaitForExit();
|
||||
}
|
||||
}
|
||||
catch (Win32Exception e)
|
||||
{
|
||||
Global.Logger.Error($"停止 {MainFile} 错误:\n" + e);
|
||||
Log.Error(e, "停止 {Name} 异常", Instance.ProcessName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -92,191 +164,17 @@ namespace Netch.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 仅初始化 <see cref="Instance" />,不设定事件处理方法
|
||||
/// </summary>
|
||||
/// <param name="argument"></param>
|
||||
protected virtual void InitInstance(string argument)
|
||||
protected virtual void OnStarted()
|
||||
{
|
||||
Instance = new Process
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
FileName = Path.GetFullPath($"bin\\{MainFile}"),
|
||||
WorkingDirectory = $"{Global.NetchDir}\\bin",
|
||||
Arguments = argument,
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = !RedirectStd,
|
||||
RedirectStandardOutput = RedirectStd,
|
||||
StandardOutputEncoding = RedirectStd ? InstanceOutputEncoding : null,
|
||||
RedirectStandardError = RedirectStd,
|
||||
StandardErrorEncoding = RedirectStd ? InstanceOutputEncoding : null,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
}
|
||||
};
|
||||
|
||||
if (!File.Exists(Instance.StartInfo.FileName))
|
||||
throw new MessageException(i18N.Translate($"bin\\{MainFile} file not found!"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 默认行为启动主程序
|
||||
/// </summary>
|
||||
/// <param name="argument">主程序启动参数</param>
|
||||
/// <param name="priority">进程优先级</param>
|
||||
/// <returns>是否成功启动</returns>
|
||||
protected void StartInstanceAuto(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal)
|
||||
{
|
||||
State = State.Starting;
|
||||
// 初始化程序
|
||||
InitInstance(argument);
|
||||
|
||||
if (RedirectToFile)
|
||||
OpenLogFile();
|
||||
|
||||
// 启动程序
|
||||
Instance!.Start();
|
||||
if (priority != ProcessPriorityClass.Normal)
|
||||
Instance.PriorityClass = priority;
|
||||
|
||||
if (RedirectStd)
|
||||
{
|
||||
Task.Run(() => ReadOutput(Instance.StandardOutput));
|
||||
Task.Run(() => ReadOutput(Instance.StandardError));
|
||||
|
||||
if (!StartedKeywords.Any())
|
||||
{
|
||||
State = State.Started;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 等待启动
|
||||
for (var i = 0; i < 1000; i++)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
switch (State)
|
||||
{
|
||||
case State.Started:
|
||||
Task.Run(OnKeywordStarted);
|
||||
return;
|
||||
case State.Stopped:
|
||||
Stop();
|
||||
CloseLogFile();
|
||||
OnKeywordStopped();
|
||||
throw new MessageException($"{Name} 控制器启动失败");
|
||||
}
|
||||
}
|
||||
|
||||
Stop();
|
||||
OnKeywordTimeout();
|
||||
throw new MessageException($"{Name} 控制器启动超时");
|
||||
}
|
||||
|
||||
#region FileStream
|
||||
|
||||
private void OpenLogFile()
|
||||
{
|
||||
if (!RedirectToFile)
|
||||
return;
|
||||
|
||||
_logFileStream = File.Open(LogPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
|
||||
_logStreamWriter = new StreamWriter(_logFileStream);
|
||||
|
||||
_flushFileStreamTimer.Elapsed += FlushFileStreamTimerEvent;
|
||||
_flushFileStreamTimer.Enabled = true;
|
||||
}
|
||||
|
||||
private void WriteLog(string line)
|
||||
{
|
||||
if (!RedirectToFile)
|
||||
return;
|
||||
|
||||
_logStreamWriter!.WriteLine(line);
|
||||
}
|
||||
|
||||
private readonly object LogStreamLock = new();
|
||||
private void CloseLogFile()
|
||||
{
|
||||
if (!RedirectToFile)
|
||||
return;
|
||||
|
||||
lock (LogStreamLock)
|
||||
{
|
||||
if (_logFileStream == null)
|
||||
return;
|
||||
|
||||
_flushFileStreamTimer.Enabled = false;
|
||||
_logStreamWriter?.Close();
|
||||
_logFileStream?.Close();
|
||||
_logStreamWriter = _logStreamWriter = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region virtual
|
||||
|
||||
protected virtual void OnReadNewLine(string line)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnKeywordStarted()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnKeywordStopped()
|
||||
protected virtual void OnStartFailed()
|
||||
{
|
||||
Utils.Utils.Open(LogPath);
|
||||
}
|
||||
|
||||
protected virtual void OnKeywordTimeout()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected void ReadOutput(TextReader reader)
|
||||
{
|
||||
string? line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
WriteLog(line);
|
||||
OnReadNewLine(line);
|
||||
|
||||
// State == State.Started if !StartedKeywords.Any()
|
||||
if (State == State.Starting)
|
||||
{
|
||||
if (StartedKeywords.Any(s => line.Contains(s)))
|
||||
State = State.Started;
|
||||
else if (StoppedKeywords.Any(s => line.Contains(s)))
|
||||
State = State.Stopped;
|
||||
}
|
||||
}
|
||||
|
||||
CloseLogFile();
|
||||
State = State.Stopped;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计时器存储日志
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void FlushFileStreamTimerEvent(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logStreamWriter!.Flush();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Global.Logger.Warning($"写入 {Name} 日志错误:\n" + exception.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,84 +3,53 @@ using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Socks5;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public static class MainController
|
||||
{
|
||||
public static Mode? Mode;
|
||||
|
||||
/// TCP or Both Server
|
||||
public static Server? Server;
|
||||
|
||||
private static Server? _udpServer;
|
||||
public static Mode? Mode { get; private set; }
|
||||
|
||||
public static readonly NTTController NTTController = new();
|
||||
private static IServerController? _serverController;
|
||||
private static IServerController? _udpServerController;
|
||||
|
||||
public static IServerController? ServerController
|
||||
{
|
||||
get => _serverController;
|
||||
private set => _serverController = value;
|
||||
}
|
||||
|
||||
public static IServerController? UdpServerController
|
||||
{
|
||||
get => _udpServerController ?? _serverController;
|
||||
set => _udpServerController = value;
|
||||
}
|
||||
|
||||
public static Server? UdpServer
|
||||
{
|
||||
get => _udpServer ?? Server;
|
||||
set => _udpServer = value;
|
||||
}
|
||||
public static IServerController? ServerController { get; private set; }
|
||||
|
||||
public static IModeController? ModeController { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
/// <exception cref="MessageException"></exception>
|
||||
public static async Task StartAsync(Server server, Mode mode)
|
||||
{
|
||||
await Task.Run(() => Start(server, mode));
|
||||
}
|
||||
|
||||
public static void Start(Server server, Mode mode)
|
||||
{
|
||||
Global.Logger.Info($"启动主控制器: {server.Type} [{(int)mode.Type}]{mode.Remark}");
|
||||
Server = server;
|
||||
Mode = mode;
|
||||
|
||||
// 刷新 DNS 缓存
|
||||
NativeMethods.RefreshDNSCache();
|
||||
Log.Information("启动主控制器: {Server} {Mode}", $"{server.Type}", $"[{(int) mode.Type}]{mode.Remark}");
|
||||
|
||||
if (DnsUtils.Lookup(server.Hostname) == null)
|
||||
throw new MessageException(i18N.Translate("Lookup Server hostname failed"));
|
||||
|
||||
// 添加 Netch 到防火墙
|
||||
Firewall.AddNetchFwRules();
|
||||
Mode = mode;
|
||||
|
||||
await Task.WhenAll(
|
||||
Task.Run(NativeMethods.RefreshDNSCache),
|
||||
Task.Run(Firewall.AddNetchFwRules)
|
||||
);
|
||||
|
||||
if (Log.IsEnabled(LogEventLevel.Debug))
|
||||
Task.Run(() =>
|
||||
{
|
||||
// TODO log level setting
|
||||
Log.Debug("Running Processes: \n{Processes}", string.Join("\n", SystemInfo.Processes(false)));
|
||||
}).Forget();
|
||||
|
||||
try
|
||||
{
|
||||
if (!ModeHelper.SkipServerController(server, mode))
|
||||
{
|
||||
StartServer(server, mode, out _serverController);
|
||||
StatusPortInfoText.UpdateShareLan();
|
||||
}
|
||||
server = await Task.Run(() => StartServer(server));
|
||||
|
||||
StartMode(mode);
|
||||
await Task.Run(() => StartMode(server, mode));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Stop();
|
||||
await StopAsync();
|
||||
|
||||
switch (e)
|
||||
{
|
||||
@@ -90,62 +59,49 @@ namespace Netch.Controllers
|
||||
case MessageException:
|
||||
throw;
|
||||
default:
|
||||
Global.Logger.Error(e.ToString());
|
||||
Global.Logger.ShowLog();
|
||||
Log.Error(e, "主控制器启动未处理异常");
|
||||
throw new MessageException($"未处理异常\n{e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void StartServer(Server server, Mode mode, out IServerController controller)
|
||||
private static Server StartServer(Server server)
|
||||
{
|
||||
controller = ServerHelper.GetUtilByTypeName(server.Type).GetController();
|
||||
ServerController = ServerHelper.GetUtilByTypeName(server.Type).GetController();
|
||||
|
||||
TryReleaseTcpPort(controller.Socks5LocalPort(), "Socks5");
|
||||
TryReleaseTcpPort(ServerController.Socks5LocalPort(), "Socks5");
|
||||
|
||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", controller.Name));
|
||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ServerController.Name));
|
||||
|
||||
controller.Start(in server, mode);
|
||||
Log.Debug($"{server.Type} {server.MaskedData()}");
|
||||
var socks5 = ServerController.Start(server);
|
||||
|
||||
if (server is Socks5 socks5)
|
||||
{
|
||||
if (socks5.Auth())
|
||||
StatusPortInfoText.Socks5Port = controller.Socks5LocalPort();
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusPortInfoText.Socks5Port = controller.Socks5LocalPort();
|
||||
}
|
||||
StatusPortInfoText.Socks5Port = socks5.Port;
|
||||
StatusPortInfoText.UpdateShareLan();
|
||||
|
||||
return socks5;
|
||||
}
|
||||
|
||||
private static void StartMode(Mode mode)
|
||||
private static void StartMode(Server server, Mode mode)
|
||||
{
|
||||
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName);
|
||||
|
||||
if (port != null)
|
||||
TryReleaseTcpPort((ushort)port, portName);
|
||||
TryReleaseTcpPort((ushort) port, portName);
|
||||
|
||||
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
|
||||
|
||||
ModeController.Start(mode);
|
||||
ModeController.Start(server, mode);
|
||||
}
|
||||
|
||||
public static async Task StopAsync()
|
||||
{
|
||||
await Task.Run(Stop);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public static void Stop()
|
||||
{
|
||||
if (_serverController == null && ModeController == null)
|
||||
if (ServerController == null && ModeController == null)
|
||||
return;
|
||||
|
||||
StatusPortInfoText.Reset();
|
||||
|
||||
_ = Task.Run(() => NTTController.Stop());
|
||||
Task.Run(() => NTTController.Stop()).Forget();
|
||||
|
||||
var tasks = new[]
|
||||
{
|
||||
@@ -155,12 +111,11 @@ namespace Netch.Controllers
|
||||
|
||||
try
|
||||
{
|
||||
Task.WaitAll(tasks);
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Error(e.ToString());
|
||||
Global.Logger.ShowLog();
|
||||
Log.Error(e, "主控制器停止未处理异常");
|
||||
}
|
||||
|
||||
ModeController = null;
|
||||
|
||||
@@ -1,29 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.ServiceProcess;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Interops;
|
||||
using Netch.Models;
|
||||
using Netch.Servers;
|
||||
using Netch.Servers.Shadowsocks;
|
||||
using Netch.Servers.Socks5;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
using static Netch.Interops.Redirector;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class NFController : IModeController
|
||||
{
|
||||
private Server? _server;
|
||||
private Mode? _mode;
|
||||
private RedirectorConfig _rdrConfig = null!;
|
||||
|
||||
private static readonly ServiceController NFService = new("netfilter2");
|
||||
|
||||
private const string BinDriver = "bin\\nfdriver.sys";
|
||||
private static readonly string SystemDriver = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
|
||||
|
||||
public string Name { get; } = "Redirector";
|
||||
public string Name => "Redirector";
|
||||
|
||||
public void Start(in Mode mode)
|
||||
public void Start(Server server, Mode mode)
|
||||
{
|
||||
_server = server;
|
||||
_mode = mode;
|
||||
_rdrConfig = Global.Settings.Redirector;
|
||||
CheckDriver();
|
||||
|
||||
Dial(NameList.TYPE_FILTERLOOPBACK, "false");
|
||||
@@ -33,18 +41,18 @@ namespace Netch.Controllers
|
||||
Dial(NameList.TYPE_UDPLISN, p.ToString());
|
||||
|
||||
// Server
|
||||
Dial(NameList.TYPE_FILTERUDP, (Global.Settings.Redirector.ProxyProtocol != PortType.TCP).ToString().ToLower());
|
||||
Dial(NameList.TYPE_FILTERTCP, (Global.Settings.Redirector.ProxyProtocol != PortType.UDP).ToString().ToLower());
|
||||
dial_Server(Global.Settings.Redirector.ProxyProtocol);
|
||||
Dial(NameList.TYPE_FILTERUDP, _rdrConfig.FilterProtocol.HasFlag(PortType.UDP).ToString().ToLower());
|
||||
Dial(NameList.TYPE_FILTERTCP, _rdrConfig.FilterProtocol.HasFlag(PortType.TCP).ToString().ToLower());
|
||||
dial_Server(_rdrConfig.FilterProtocol, _server);
|
||||
|
||||
// Mode Rule
|
||||
dial_Name(mode);
|
||||
dial_Name(_mode);
|
||||
|
||||
// Features
|
||||
Dial(NameList.TYPE_DNSHOST, Global.Settings.Redirector.DNSHijack ? Global.Settings.Redirector.DNSHijackHost : "");
|
||||
Dial(NameList.TYPE_DNSHOST, _rdrConfig.DNSHijack ? _rdrConfig.DNSHijackHost : "");
|
||||
|
||||
if (!Init())
|
||||
throw new MessageException("Redirector Start failed, run Netch with \"-console\" argument");
|
||||
throw new MessageException("Redirector start failed.");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
@@ -94,31 +102,16 @@ namespace Netch.Controllers
|
||||
|
||||
#endregion
|
||||
|
||||
private void dial_Server(in PortType portType)
|
||||
private void dial_Server(PortType portType, in Server server)
|
||||
{
|
||||
if (portType == PortType.Both)
|
||||
{
|
||||
dial_Server(PortType.TCP);
|
||||
dial_Server(PortType.UDP);
|
||||
dial_Server(PortType.TCP, server);
|
||||
dial_Server(PortType.UDP, server);
|
||||
return;
|
||||
}
|
||||
|
||||
int offset;
|
||||
Server server;
|
||||
IServerController controller;
|
||||
|
||||
if (portType == PortType.UDP)
|
||||
{
|
||||
offset = UdpNameListOffset;
|
||||
server = MainController.UdpServer!;
|
||||
controller = MainController.UdpServerController!;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 0;
|
||||
server = MainController.Server!;
|
||||
controller = MainController.ServerController!;
|
||||
}
|
||||
var offset = portType == PortType.UDP ? UdpNameListOffset : 0;
|
||||
|
||||
if (server is Socks5 socks5)
|
||||
{
|
||||
@@ -128,7 +121,7 @@ namespace Netch.Controllers
|
||||
Dial(NameList.TYPE_TCPPASS + offset, socks5.Password ?? string.Empty);
|
||||
Dial(NameList.TYPE_TCPMETH + offset, string.Empty);
|
||||
}
|
||||
else if (server is Shadowsocks shadowsocks && !shadowsocks.HasPlugin() && Global.Settings.Redirector.RedirectorSS)
|
||||
else if (server is Shadowsocks shadowsocks && !shadowsocks.HasPlugin() && _rdrConfig.RedirectorSS)
|
||||
{
|
||||
Dial(NameList.TYPE_TCPTYPE + offset, "Shadowsocks");
|
||||
Dial(NameList.TYPE_TCPHOST + offset, $"{shadowsocks.AutoResolveHostname()}:{shadowsocks.Port}");
|
||||
@@ -137,11 +130,7 @@ namespace Netch.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
Dial(NameList.TYPE_TCPTYPE + offset, "Socks5");
|
||||
Dial(NameList.TYPE_TCPHOST + offset, $"127.0.0.1:{controller.Socks5LocalPort()}");
|
||||
Dial(NameList.TYPE_TCPUSER + offset, string.Empty);
|
||||
Dial(NameList.TYPE_TCPPASS + offset, string.Empty);
|
||||
Dial(NameList.TYPE_TCPMETH + offset, string.Empty);
|
||||
Trace.Assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,11 +163,11 @@ namespace Netch.Controllers
|
||||
|
||||
private static void CheckDriver()
|
||||
{
|
||||
var binFileVersion = Utils.Utils.GetFileVersion(BinDriver);
|
||||
var binFileVersion = Utils.Utils.GetFileVersion(Constants.NFDriver);
|
||||
var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
|
||||
|
||||
Global.Logger.Info("内置驱动版本: " + binFileVersion);
|
||||
Global.Logger.Info("系统驱动版本: " + systemFileVersion);
|
||||
Log.Information("内置驱动版本: {Name}", binFileVersion);
|
||||
Log.Information("系统驱动版本: {Name}", systemFileVersion);
|
||||
|
||||
if (!File.Exists(SystemDriver))
|
||||
{
|
||||
@@ -208,7 +197,7 @@ namespace Netch.Controllers
|
||||
if (!reinstall)
|
||||
return;
|
||||
|
||||
Global.Logger.Info("更新驱动");
|
||||
Log.Information("更新驱动");
|
||||
UninstallDriver();
|
||||
InstallDriver();
|
||||
}
|
||||
@@ -219,18 +208,18 @@ namespace Netch.Controllers
|
||||
/// <returns>驱动是否安装成功</returns>
|
||||
private static void InstallDriver()
|
||||
{
|
||||
Global.Logger.Info("安装 NF 驱动");
|
||||
Log.Information("安装 NF 驱动");
|
||||
|
||||
if (!File.Exists(BinDriver))
|
||||
if (!File.Exists(Constants.NFDriver))
|
||||
throw new MessageException(i18N.Translate("builtin driver files missing, can't install NF driver"));
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(BinDriver, SystemDriver);
|
||||
File.Copy(Constants.NFDriver, SystemDriver);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Error("驱动复制失败\n" + e);
|
||||
Log.Error(e, "驱动复制失败\n");
|
||||
throw new MessageException($"Copy NF driver file failed\n{e.Message}");
|
||||
}
|
||||
|
||||
@@ -239,11 +228,11 @@ namespace Netch.Controllers
|
||||
var result = NFAPI.nf_registerDriver("netfilter2");
|
||||
if (result == NF_STATUS.NF_STATUS_SUCCESS)
|
||||
{
|
||||
Global.Logger.Info("驱动安装成功");
|
||||
Log.Information("驱动安装成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.Logger.Error($"注册驱动失败,返回值:{result}");
|
||||
Log.Error("注册驱动失败: {Result}", result);
|
||||
throw new MessageException($"Register NF driver failed\n{result}");
|
||||
}
|
||||
}
|
||||
@@ -254,7 +243,7 @@ namespace Netch.Controllers
|
||||
/// <returns>是否成功卸载</returns>
|
||||
public static bool UninstallDriver()
|
||||
{
|
||||
Global.Logger.Info("卸载 NF 驱动");
|
||||
Log.Information("卸载 NF 驱动");
|
||||
try
|
||||
{
|
||||
if (NFService.Status == ServiceControllerStatus.Running)
|
||||
|
||||
@@ -4,50 +4,47 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class NTTController : Guard, IController
|
||||
{
|
||||
public override string MainFile { get; protected set; } = "NTT.exe";
|
||||
|
||||
public override string Name { get; } = "NTT";
|
||||
|
||||
public override void Stop()
|
||||
public NTTController() : base("NTT.exe")
|
||||
{
|
||||
StopInstance();
|
||||
}
|
||||
|
||||
public override string Name => "NTT";
|
||||
|
||||
/// <summary>
|
||||
/// 启动 NatTypeTester
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<(string?, string?, string?)> Start()
|
||||
public async Task<(string? result, string? localEnd, string? publicEnd)> Start()
|
||||
{
|
||||
string? localEnd = null, publicEnd = null, result = null, bindingTest = null;
|
||||
|
||||
try
|
||||
{
|
||||
InitInstance($" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}");
|
||||
Instance!.Start();
|
||||
Instance.StartInfo.Arguments = $" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}";
|
||||
Instance.Start();
|
||||
|
||||
var output = await Instance.StandardOutput.ReadToEndAsync();
|
||||
var error = await Instance.StandardError.ReadToEndAsync();
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(Path.Combine(Global.NetchDir, $"logging\\{Name}.log"), $"{output}\r\n{error}");
|
||||
await File.WriteAllTextAsync(Path.Combine(Global.NetchDir, $"logging\\{Name}.log"), $"{output}\r\n{error}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Warning($"写入 {Name} 日志错误:\n" + e.Message);
|
||||
Log.Warning(e, "写入 {Name} 日志错误", Name);
|
||||
}
|
||||
|
||||
if (output.IsNullOrWhiteSpace())
|
||||
if (!error.IsNullOrWhiteSpace())
|
||||
{
|
||||
error = error.Trim();
|
||||
var errorFirst = error.Substring(0, error.IndexOf('\n')).Trim();
|
||||
var errorFirst = error.GetLines().First();
|
||||
return (errorFirst.SplitTrimEntries(':').Last(), null, null);
|
||||
}
|
||||
|
||||
@@ -87,7 +84,7 @@ namespace Netch.Controllers
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Error($"{Name} 控制器出错:\n" + e);
|
||||
Log.Error(e, "{Name} 控制器启动异常", Name);
|
||||
try
|
||||
{
|
||||
Stop();
|
||||
|
||||
@@ -8,59 +8,72 @@ using System.Threading.Tasks;
|
||||
using Netch.Forms;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Socks5;
|
||||
using Netch.Servers;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class PcapController : Guard, IModeController
|
||||
{
|
||||
public override string Name { get; } = "pcap2socks";
|
||||
private readonly LogForm _form;
|
||||
private Mode? _mode;
|
||||
private Server? _server;
|
||||
|
||||
public override string MainFile { get; protected set; } = "pcap2socks.exe";
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] { "└" };
|
||||
|
||||
protected override Encoding? InstanceOutputEncoding { get; } = Encoding.UTF8;
|
||||
|
||||
private LogForm? _form;
|
||||
|
||||
public void Start(in Mode mode)
|
||||
public PcapController() : base("pcap2socks.exe", encoding: Encoding.UTF8)
|
||||
{
|
||||
var server = MainController.Server!;
|
||||
|
||||
_form = new LogForm(Global.MainForm);
|
||||
_form.CreateControl();
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; } = new[] { "└" };
|
||||
|
||||
public override string Name => "pcap2socks";
|
||||
|
||||
public void Start(Server server, Mode mode)
|
||||
{
|
||||
_server = server;
|
||||
_mode = mode;
|
||||
|
||||
var outboundNetworkInterface = NetworkInterfaceUtils.GetBest();
|
||||
|
||||
var argument = new StringBuilder($@"-i \Device\NPF_{outboundNetworkInterface.Id}");
|
||||
if (server is Socks5 socks5 && !socks5.Auth())
|
||||
argument.Append($" --destination {server.AutoResolveHostname()}:{server.Port}");
|
||||
if (_server is Socks5 socks5 && !socks5.Auth())
|
||||
argument.Append($" --destination {socks5.AutoResolveHostname()}:{socks5.Port}");
|
||||
else
|
||||
argument.Append($" --destination 127.0.0.1:{Global.Settings.Socks5LocalPort}");
|
||||
|
||||
argument.Append($" {mode.GetRules().FirstOrDefault() ?? "-P n"}");
|
||||
StartInstanceAuto(argument.ToString());
|
||||
argument.Append($" {_mode.GetRules().FirstOrDefault() ?? "-P n"}");
|
||||
StartGuard(argument.ToString());
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
Global.MainForm.Invoke(new Action(() => { _form.Close(); }));
|
||||
StopGuard();
|
||||
}
|
||||
|
||||
~PcapController()
|
||||
{
|
||||
_form.Dispose();
|
||||
}
|
||||
|
||||
protected override void OnReadNewLine(string line)
|
||||
{
|
||||
Global.MainForm.BeginInvoke(new Action(() =>
|
||||
{
|
||||
if (!_form!.IsDisposed)
|
||||
if (!_form.IsDisposed)
|
||||
_form.richTextBox1.AppendText(line + "\n");
|
||||
}));
|
||||
}
|
||||
|
||||
protected override void OnKeywordStarted()
|
||||
protected override void OnStarted()
|
||||
{
|
||||
Global.MainForm.BeginInvoke(new Action(() => { _form!.Show(); }));
|
||||
Global.MainForm.BeginInvoke(new Action(() => _form.Show()));
|
||||
}
|
||||
|
||||
protected override void OnKeywordStopped()
|
||||
protected override void OnStartFailed()
|
||||
{
|
||||
if (File.ReadAllText(LogPath).Length == 0)
|
||||
if (new FileInfo(LogPath).Length == 0)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
@@ -73,11 +86,5 @@ namespace Netch.Controllers
|
||||
|
||||
Utils.Utils.Open(LogPath);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
_form!.Close();
|
||||
StopInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,51 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Enums;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Socks5;
|
||||
using Netch.Utils;
|
||||
using Netch.Interops;
|
||||
using Netch.Models;
|
||||
using Netch.Servers;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
using static Netch.Interops.tun2socks;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class TUNController : IModeController
|
||||
{
|
||||
public string Name => "tun2socks";
|
||||
|
||||
private const string DummyDns = "6.6.6.6";
|
||||
|
||||
private readonly DNSController _aioDnsController = new();
|
||||
|
||||
private NetRoute _outbound;
|
||||
private Mode _mode = null!;
|
||||
private IPAddress? _serverRemoteAddress;
|
||||
private TUNConfig _tunConfig = null!;
|
||||
|
||||
private NetRoute _tun;
|
||||
private NetRoute _outbound;
|
||||
|
||||
private IPAddress _serverAddresses = null!;
|
||||
public string Name => "tun2socks";
|
||||
|
||||
private Mode _mode = null!;
|
||||
|
||||
public void Start(in Mode mode)
|
||||
public void Start(Server server, Mode mode)
|
||||
{
|
||||
_mode = mode;
|
||||
var server = MainController.Server!;
|
||||
_serverAddresses = DnsUtils.Lookup(server.Hostname)!; // server address have been cached when MainController.Start
|
||||
_tunConfig = Global.Settings.TUNTAP;
|
||||
|
||||
IPAddress address;
|
||||
(_outbound, address) = NetRoute.GetBestRouteTemplate();
|
||||
if (server is Socks5Bridge socks5Bridge)
|
||||
_serverRemoteAddress = DnsUtils.Lookup(socks5Bridge.RemoteHostname);
|
||||
|
||||
if (_serverRemoteAddress != null && IPAddress.IsLoopback(_serverRemoteAddress))
|
||||
_serverRemoteAddress = null;
|
||||
|
||||
_outbound = NetRoute.GetBestRouteTemplate();
|
||||
CheckDriver();
|
||||
|
||||
Dial(NameList.TYPE_ADAPMTU, "1500");
|
||||
Dial(NameList.TYPE_BYPBIND, address.ToString());
|
||||
Dial(NameList.TYPE_BYPBIND, _outbound.Gateway);
|
||||
Dial(NameList.TYPE_BYPLIST, "disabled");
|
||||
|
||||
#region Server
|
||||
@@ -53,9 +58,9 @@ namespace Netch.Controllers
|
||||
|
||||
if (server is Socks5 socks5)
|
||||
{
|
||||
Dial(NameList.TYPE_TCPHOST, $"{server.AutoResolveHostname()}:{server.Port}");
|
||||
Dial(NameList.TYPE_TCPHOST, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
|
||||
|
||||
Dial(NameList.TYPE_UDPHOST, $"{server.AutoResolveHostname()}:{server.Port}");
|
||||
Dial(NameList.TYPE_UDPHOST, $"{socks5.AutoResolveHostname()}:{socks5.Port}");
|
||||
|
||||
if (socks5.Auth())
|
||||
{
|
||||
@@ -68,22 +73,19 @@ namespace Netch.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
Dial(NameList.TYPE_TCPHOST, $"127.0.0.1:{Global.Settings.Socks5LocalPort}");
|
||||
|
||||
Dial(NameList.TYPE_UDPHOST, $"127.0.0.1:{Global.Settings.Socks5LocalPort}");
|
||||
Trace.Assert(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DNS
|
||||
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
if (_tunConfig.UseCustomDNS)
|
||||
{
|
||||
Dial(NameList.TYPE_DNSADDR, Global.Settings.TUNTAP.HijackDNS);
|
||||
Dial(NameList.TYPE_DNSADDR, _tunConfig.HijackDNS);
|
||||
}
|
||||
else
|
||||
{
|
||||
MainController.PortCheck(Global.Settings.AioDNS.ListenPort, "DNS");
|
||||
_aioDnsController.Start();
|
||||
Dial(NameList.TYPE_DNSADDR, $"127.0.0.1:{Global.Settings.AioDNS.ListenPort}");
|
||||
}
|
||||
@@ -91,85 +93,19 @@ namespace Netch.Controllers
|
||||
#endregion
|
||||
|
||||
if (!Init())
|
||||
throw new MessageException("tun2socks start failed, reboot your system and start again.");
|
||||
throw new MessageException("tun2socks start failed.");
|
||||
|
||||
var tunIndex = (int)RouteHelper.ConvertLuidToIndex(tun_luid());
|
||||
_tun = NetRoute.TemplateBuilder(Global.Settings.TUNTAP.Gateway, tunIndex);
|
||||
_tun = NetRoute.TemplateBuilder(_tunConfig.Gateway, tunIndex);
|
||||
|
||||
RouteHelper.CreateUnicastIP(AddressFamily.InterNetwork,
|
||||
Global.Settings.TUNTAP.Address,
|
||||
(byte)Utils.Utils.SubnetToCidr(Global.Settings.TUNTAP.Netmask),
|
||||
_tunConfig.Address,
|
||||
(byte)Utils.Utils.SubnetToCidr(_tunConfig.Netmask),
|
||||
(ulong)tunIndex);
|
||||
|
||||
SetupRouteTable(mode);
|
||||
SetupRouteTable();
|
||||
}
|
||||
|
||||
#region Route
|
||||
|
||||
private void SetupRouteTable(Mode mode)
|
||||
{
|
||||
Global.MainForm.StatusText(i18N.Translate("Setup Route Table Rule"));
|
||||
Global.Logger.Info("设置路由规则");
|
||||
|
||||
// Server Address
|
||||
if (!IPAddress.IsLoopback(_serverAddresses))
|
||||
RouteUtils.CreateRoute(_outbound.FillTemplate(_serverAddresses.ToString(), 32));
|
||||
|
||||
// Global Bypass IPs
|
||||
RouteUtils.CreateRouteFill(_outbound, Global.Settings.TUNTAP.BypassIPs);
|
||||
|
||||
var tunNetworkInterface = NetworkInterfaceUtils.Get(_tun.InterfaceIndex);
|
||||
switch (mode.Type)
|
||||
{
|
||||
case ModeType.ProxyRuleIPs:
|
||||
// rules
|
||||
RouteUtils.CreateRouteFill(_tun, mode.GetRules());
|
||||
|
||||
if (Global.Settings.TUNTAP.ProxyDNS)
|
||||
{
|
||||
tunNetworkInterface.SetDns(DummyDns);
|
||||
// proxy dummy dns
|
||||
RouteUtils.CreateRoute(_tun.FillTemplate(DummyDns, 32));
|
||||
|
||||
if (!Global.Settings.TUNTAP.UseCustomDNS)
|
||||
// proxy AioDNS other dns
|
||||
RouteUtils.CreateRoute(_tun.FillTemplate(Utils.Utils.GetHostFromUri(Global.Settings.AioDNS.OtherDNS), 32));
|
||||
}
|
||||
|
||||
break;
|
||||
case ModeType.BypassRuleIPs:
|
||||
RouteUtils.CreateRouteFill(_outbound, mode.GetRules());
|
||||
|
||||
tunNetworkInterface.SetDns(DummyDns);
|
||||
|
||||
if (!Global.Settings.TUNTAP.UseCustomDNS)
|
||||
// bypass AioDNS other dns
|
||||
RouteUtils.CreateRoute(_tun.FillTemplate(Utils.Utils.GetHostFromUri(Global.Settings.AioDNS.ChinaDNS), 32));
|
||||
|
||||
NetworkInterfaceUtils.SetInterfaceMetric(_tun.InterfaceIndex, 0);
|
||||
RouteUtils.CreateRoute(_tun.FillTemplate("0.0.0.0", 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearRouteTable()
|
||||
{
|
||||
if (!IPAddress.IsLoopback(_serverAddresses))
|
||||
RouteUtils.DeleteRoute(_outbound.FillTemplate(_serverAddresses.ToString(), 32));
|
||||
|
||||
RouteUtils.DeleteRouteFill(_outbound, Global.Settings.TUNTAP.BypassIPs);
|
||||
|
||||
switch (_mode.Type)
|
||||
{
|
||||
case ModeType.BypassRuleIPs:
|
||||
RouteUtils.DeleteRouteFill(_outbound, _mode.GetRules());
|
||||
NetworkInterfaceUtils.SetInterfaceMetric(_outbound.InterfaceIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
var tasks = new[]
|
||||
@@ -184,26 +120,92 @@ namespace Netch.Controllers
|
||||
|
||||
private void CheckDriver()
|
||||
{
|
||||
string binDriver = Path.Combine(Global.NetchDir, @"bin\wintun.dll");
|
||||
string binDriver = Path.Combine(Global.NetchDir, Constants.WintunDllFile);
|
||||
string sysDriver = $@"{Environment.SystemDirectory}\wintun.dll";
|
||||
|
||||
var binHash = Utils.Utils.SHA256CheckSum(binDriver);
|
||||
var sysHash = Utils.Utils.SHA256CheckSum(sysDriver);
|
||||
Global.Logger.Info("自带 wintun.dll Hash: " + binHash);
|
||||
Global.Logger.Info("系统 wintun.dll Hash: " + sysHash);
|
||||
Log.Information("自带 wintun.dll Hash: {Hash}", binHash);
|
||||
Log.Information("系统 wintun.dll Hash: {Hash}", sysHash);
|
||||
if (binHash == sysHash)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
Global.Logger.Info("Copy wintun.dll to System Directory");
|
||||
Log.Information("Copy wintun.dll to System Directory");
|
||||
File.Copy(binDriver, sysDriver, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Error(e.ToString());
|
||||
Log.Error(e, "复制 wintun.dll 异常");
|
||||
throw new MessageException($"Failed to copy wintun.dll to system directory: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#region Route
|
||||
|
||||
private void SetupRouteTable()
|
||||
{
|
||||
Global.MainForm.StatusText(i18N.Translate("Setup Route Table Rule"));
|
||||
Log.Information("设置路由规则");
|
||||
|
||||
// Server Address
|
||||
if (_serverRemoteAddress != null)
|
||||
RouteUtils.CreateRoute(_outbound.FillTemplate(_serverRemoteAddress.ToString(), 32));
|
||||
|
||||
// Global Bypass IPs
|
||||
RouteUtils.CreateRouteFill(_outbound, _tunConfig.BypassIPs);
|
||||
|
||||
var tunNetworkInterface = NetworkInterfaceUtils.Get(_tun.InterfaceIndex);
|
||||
switch (_mode.Type)
|
||||
{
|
||||
case ModeType.ProxyRuleIPs:
|
||||
// rules
|
||||
RouteUtils.CreateRouteFill(_tun, _mode.GetRules());
|
||||
|
||||
if (_tunConfig.ProxyDNS)
|
||||
{
|
||||
tunNetworkInterface.SetDns(DummyDns);
|
||||
// proxy dummy dns
|
||||
RouteUtils.CreateRoute(_tun.FillTemplate(DummyDns, 32));
|
||||
|
||||
if (!_tunConfig.UseCustomDNS)
|
||||
// proxy AioDNS other dns
|
||||
RouteUtils.CreateRoute(_tun.FillTemplate(Utils.Utils.GetHostFromUri(Global.Settings.AioDNS.OtherDNS), 32));
|
||||
}
|
||||
|
||||
break;
|
||||
case ModeType.BypassRuleIPs:
|
||||
RouteUtils.CreateRouteFill(_outbound, _mode.GetRules());
|
||||
|
||||
tunNetworkInterface.SetDns(DummyDns);
|
||||
|
||||
if (!_tunConfig.UseCustomDNS)
|
||||
// bypass AioDNS other dns
|
||||
RouteUtils.CreateRoute(_outbound.FillTemplate(Utils.Utils.GetHostFromUri(Global.Settings.AioDNS.ChinaDNS), 32));
|
||||
|
||||
NetworkInterfaceUtils.SetInterfaceMetric(_tun.InterfaceIndex, 0);
|
||||
RouteUtils.CreateRoute(_tun.FillTemplate("0.0.0.0", 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearRouteTable()
|
||||
{
|
||||
if (_serverRemoteAddress != null)
|
||||
RouteUtils.DeleteRoute(_outbound.FillTemplate(_serverRemoteAddress.ToString(), 32));
|
||||
|
||||
RouteUtils.DeleteRouteFill(_outbound, Global.Settings.TUNTAP.BypassIPs);
|
||||
|
||||
switch (_mode.Type)
|
||||
{
|
||||
case ModeType.BypassRuleIPs:
|
||||
RouteUtils.DeleteRouteFill(_outbound, _mode.GetRules());
|
||||
NetworkInterfaceUtils.SetInterfaceMetric(_outbound.InterfaceIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using Netch.Models.GitHubRelease;
|
||||
using Netch.Utils;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -8,6 +6,9 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Models.GitHubRelease;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
@@ -19,7 +20,7 @@ namespace Netch.Controllers
|
||||
public const string Name = @"Netch";
|
||||
public const string Copyright = @"Copyright © 2019 - 2021";
|
||||
|
||||
public const string AssemblyVersion = @"1.8.4";
|
||||
public const string AssemblyVersion = @"1.8.6";
|
||||
private const string Suffix = @"";
|
||||
|
||||
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
|
||||
@@ -47,37 +48,32 @@ namespace Netch.Controllers
|
||||
|
||||
var releases = JsonSerializer.Deserialize<List<Release>>(json)!;
|
||||
LatestRelease = GetLatestRelease(releases, isPreRelease);
|
||||
Global.Logger.Info($"Github 最新发布版本: {LatestRelease.tag_name}");
|
||||
Log.Information("Github 最新发布版本: {Version}", LatestRelease.tag_name);
|
||||
if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0)
|
||||
{
|
||||
Global.Logger.Info("发现新版本");
|
||||
Log.Information("发现新版本");
|
||||
NewVersionFound?.Invoke(null, new EventArgs());
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.Logger.Info("目前是最新版本");
|
||||
Log.Information("目前是最新版本");
|
||||
NewVersionNotFound?.Invoke(null, new EventArgs());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is WebException)
|
||||
Global.Logger.Warning($"获取新版本失败: {e.Message}");
|
||||
Log.Warning(e, "获取新版本失败");
|
||||
else
|
||||
Global.Logger.Warning(e.ToString());
|
||||
Log.Error(e, "获取新版本异常");
|
||||
|
||||
NewVersionFoundFailed?.Invoke(null, new EventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetLatestUpdateFileNameAndHash(out string fileName, out string sha256, string? keyword = null)
|
||||
public static (string fileName, string sha256) GetLatestUpdateFileNameAndHash(string? keyword = null)
|
||||
{
|
||||
fileName = string.Empty;
|
||||
sha256 = string.Empty;
|
||||
|
||||
var matches = Regex.Matches(LatestRelease.body, @"^\| (?<filename>.*) \| (?<sha256>.*) \|\r?$", RegexOptions.Multiline)
|
||||
.Cast<Match>()
|
||||
.Skip(2);
|
||||
var matches = Regex.Matches(LatestRelease.body, @"^\| (?<filename>.*) \| (?<sha256>.*) \|\r?$", RegexOptions.Multiline).Skip(2);
|
||||
/*
|
||||
Skip(2)
|
||||
|
||||
@@ -87,8 +83,7 @@ namespace Netch.Controllers
|
||||
|
||||
Match match = keyword == null ? matches.First() : matches.First(m => m.Groups["filename"].Value.Contains(keyword));
|
||||
|
||||
fileName = match.Groups["filename"].Value;
|
||||
sha256 = match.Groups["sha256"].Value;
|
||||
return (match.Groups["filename"].Value, match.Groups["sha256"].Value);
|
||||
}
|
||||
|
||||
public static string GetLatestReleaseContent()
|
||||
@@ -105,7 +100,7 @@ namespace Netch.Controllers
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static Release GetLatestRelease(IEnumerable<Release> releases, bool isPreRelease)
|
||||
private static Release GetLatestRelease(IEnumerable<Release> releases, bool isPreRelease)
|
||||
{
|
||||
if (!isPreRelease)
|
||||
releases = releases.Where(release => !release.prerelease);
|
||||
|
||||
@@ -50,13 +50,13 @@ namespace Netch.Forms
|
||||
MessageBoxX.Show(i18N.Translate("Please select an IP"));
|
||||
}
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
private async void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Global.Settings.TUNTAP.BypassIPs.Clear();
|
||||
foreach (var ip in IPListBox.Items)
|
||||
Global.Settings.TUNTAP.BypassIPs.Add((string)ip);
|
||||
|
||||
Configuration.Save();
|
||||
await Configuration.SaveAsync();
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Close();
|
||||
}
|
||||
|
||||
2
Netch/Forms/LogForm.Designer.cs
generated
2
Netch/Forms/LogForm.Designer.cs
generated
@@ -74,7 +74,7 @@ namespace Netch.Forms
|
||||
this.ShowIcon = false;
|
||||
this.ShowInTaskbar = false;
|
||||
this.Text = "LogForm";
|
||||
this.Load += new System.EventHandler(this.Notifycation_Load);
|
||||
this.Load += new System.EventHandler(this.LogForm_Load);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Netch.Forms
|
||||
richTextBox1.ScrollToCaret();
|
||||
}
|
||||
|
||||
private void Notifycation_Load(object? sender, EventArgs? e)
|
||||
private void LogForm_Load(object? sender, EventArgs? e)
|
||||
{
|
||||
_parent.LocationChanged += Parent_Move;
|
||||
_parent.SizeChanged += Parent_Move;
|
||||
|
||||
18
Netch/Forms/MainForm.Designer.cs
generated
18
Netch/Forms/MainForm.Designer.cs
generated
@@ -40,6 +40,7 @@
|
||||
this.UpdateServersFromSubscribeLinksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.OptionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.OpenDirectoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ShowHideConsoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.CleanDNSCacheToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.UninstallServiceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.removeNetchFirewallRulesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
@@ -190,6 +191,7 @@
|
||||
//
|
||||
this.OptionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.OpenDirectoryToolStripMenuItem,
|
||||
this.ShowHideConsoleToolStripMenuItem,
|
||||
this.CleanDNSCacheToolStripMenuItem,
|
||||
this.UninstallServiceToolStripMenuItem,
|
||||
this.removeNetchFirewallRulesToolStripMenuItem});
|
||||
@@ -205,6 +207,13 @@
|
||||
this.OpenDirectoryToolStripMenuItem.Text = "Open Directory";
|
||||
this.OpenDirectoryToolStripMenuItem.Click += new System.EventHandler(this.OpenDirectoryToolStripMenuItem_Click);
|
||||
//
|
||||
// ShowHideConsoleToolStripMenuItem
|
||||
//
|
||||
this.ShowHideConsoleToolStripMenuItem.Name = "ShowHideConsoleToolStripMenuItem";
|
||||
this.ShowHideConsoleToolStripMenuItem.Size = new System.Drawing.Size(243, 22);
|
||||
this.ShowHideConsoleToolStripMenuItem.Text = "Show/Hide Console";
|
||||
this.ShowHideConsoleToolStripMenuItem.Click += new System.EventHandler(this.ShowHideConsoleToolStripMenuItem_Click);
|
||||
//
|
||||
// CleanDNSCacheToolStripMenuItem
|
||||
//
|
||||
this.CleanDNSCacheToolStripMenuItem.Name = "CleanDNSCacheToolStripMenuItem";
|
||||
@@ -600,19 +609,19 @@
|
||||
this.ExitToolStripButton});
|
||||
this.NotifyMenu.Name = "NotifyMenu";
|
||||
this.NotifyMenu.ShowItemToolTips = false;
|
||||
this.NotifyMenu.Size = new System.Drawing.Size(108, 48);
|
||||
this.NotifyMenu.Size = new System.Drawing.Size(181, 70);
|
||||
//
|
||||
// ShowMainFormToolStripButton
|
||||
//
|
||||
this.ShowMainFormToolStripButton.Name = "ShowMainFormToolStripButton";
|
||||
this.ShowMainFormToolStripButton.Size = new System.Drawing.Size(107, 22);
|
||||
this.ShowMainFormToolStripButton.Size = new System.Drawing.Size(180, 22);
|
||||
this.ShowMainFormToolStripButton.Text = "Show";
|
||||
this.ShowMainFormToolStripButton.Click += new System.EventHandler(this.ShowMainFormToolStripButton_Click);
|
||||
//
|
||||
// ExitToolStripButton
|
||||
//
|
||||
this.ExitToolStripButton.Name = "ExitToolStripButton";
|
||||
this.ExitToolStripButton.Size = new System.Drawing.Size(107, 22);
|
||||
this.ExitToolStripButton.Size = new System.Drawing.Size(180, 22);
|
||||
this.ExitToolStripButton.Text = "Exit";
|
||||
this.ExitToolStripButton.Click += new System.EventHandler(this.ExitToolStripButton_Click);
|
||||
//
|
||||
@@ -743,7 +752,7 @@
|
||||
private System.Windows.Forms.ToolStripMenuItem ImportServersFromClipboardToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem ManageSubscribeLinksToolStripMenuItem;
|
||||
private System.Windows.Forms.MenuStrip MenuStrip;
|
||||
private System.Windows.Forms.ComboBox ModeComboBox;
|
||||
public System.Windows.Forms.ComboBox ModeComboBox;
|
||||
private System.Windows.Forms.Label ModeLabel;
|
||||
private System.Windows.Forms.ToolStripMenuItem ModeToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem HelpToolStripMenuItem;
|
||||
@@ -782,5 +791,6 @@
|
||||
|
||||
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
|
||||
private System.Windows.Forms.ContainerControl ButtomControlContainerControl;
|
||||
private System.Windows.Forms.ToolStripMenuItem ShowHideConsoleToolStripMenuItem;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
using Microsoft.Win32;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms.Mode;
|
||||
using Netch.Models;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
@@ -14,8 +9,17 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32;
|
||||
using Netch.Controllers;
|
||||
using Netch.Enums;
|
||||
using Netch.Forms.ModeForms;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Properties;
|
||||
using Netch.Services;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace Netch.Forms
|
||||
{
|
||||
@@ -42,8 +46,6 @@ namespace Netch.Forms
|
||||
|
||||
// 监听电源事件
|
||||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
||||
|
||||
CheckForIllegalCrossThreadCalls = false;
|
||||
}
|
||||
|
||||
private void AddAddServerToolStripMenuItems()
|
||||
@@ -71,10 +73,13 @@ namespace Netch.Forms
|
||||
RecordSize();
|
||||
|
||||
LoadServers();
|
||||
SelectLastServer();
|
||||
ServerHelper.DelayTestHelper.UpdateInterval();
|
||||
|
||||
ModeHelper.InitWatcher();
|
||||
ModeHelper.Load();
|
||||
LoadModes();
|
||||
SelectLastMode();
|
||||
|
||||
// 加载翻译
|
||||
TranslateControls();
|
||||
@@ -85,23 +90,25 @@ namespace Netch.Forms
|
||||
// 加载快速配置
|
||||
LoadProfiles();
|
||||
|
||||
Task.Run(() =>
|
||||
BeginInvoke(new Action(async () =>
|
||||
{
|
||||
// 检查更新
|
||||
if (Global.Settings.CheckUpdateWhenOpened)
|
||||
CheckUpdate();
|
||||
});
|
||||
await CheckUpdate();
|
||||
}));
|
||||
|
||||
Task.Run(() =>
|
||||
BeginInvoke(new Action(async () =>
|
||||
{
|
||||
// 检查订阅更新
|
||||
if (Global.Settings.UpdateServersWhenOpened)
|
||||
UpdateServersFromSubscribe().Wait();
|
||||
await UpdateServersFromSubscribe();
|
||||
|
||||
// 打开软件时启动加速,产生开始按钮点击事件
|
||||
if (Global.Settings.StartWhenOpened)
|
||||
ControlButton_Click(null, null);
|
||||
});
|
||||
}));
|
||||
|
||||
Netch.SingleInstance.ListenForArgumentsFromSuccessiveInstances();
|
||||
}
|
||||
|
||||
private void RecordSize()
|
||||
@@ -204,7 +211,7 @@ namespace Netch.Forms
|
||||
|
||||
#region Server
|
||||
|
||||
private void ImportServersFromClipboardToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
private async void ImportServersFromClipboardToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var texts = Clipboard.GetText();
|
||||
if (!string.IsNullOrWhiteSpace(texts))
|
||||
@@ -214,11 +221,11 @@ namespace Netch.Forms
|
||||
NotifyTip(i18N.TranslateFormat("Import {0} server(s) form Clipboard", servers.Count));
|
||||
|
||||
LoadServers();
|
||||
Configuration.Save();
|
||||
await Configuration.SaveAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddServerToolStripMenuItem_Click([NotNull] object? sender, EventArgs? e)
|
||||
private async void AddServerToolStripMenuItem_Click([NotNull] object? sender, EventArgs? e)
|
||||
{
|
||||
if (sender == null)
|
||||
throw new ArgumentNullException(nameof(sender));
|
||||
@@ -229,7 +236,7 @@ namespace Netch.Forms
|
||||
util.Create();
|
||||
|
||||
LoadServers();
|
||||
Configuration.Save();
|
||||
await Configuration.SaveAsync();
|
||||
Show();
|
||||
}
|
||||
|
||||
@@ -240,14 +247,14 @@ namespace Netch.Forms
|
||||
private void CreateProcessModeToolStripButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Hide();
|
||||
new Process().ShowDialog();
|
||||
new ProcessForm().ShowDialog();
|
||||
Show();
|
||||
}
|
||||
|
||||
private void createRouteTableModeToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Hide();
|
||||
new Route().ShowDialog();
|
||||
new RouteForm().ShowDialog();
|
||||
Show();
|
||||
}
|
||||
|
||||
@@ -289,13 +296,13 @@ namespace Netch.Forms
|
||||
await Subscription.UpdateServersAsync();
|
||||
|
||||
LoadServers();
|
||||
Configuration.Save();
|
||||
await Configuration.SaveAsync();
|
||||
StatusText(i18N.Translate("Subscription updated"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
NotifyTip(i18N.Translate("update servers failed") + "\n" + e.Message, info: false);
|
||||
Global.Logger.Error("更新服务器 失败!" + e);
|
||||
Log.Error("更新服务器 失败!" + e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -307,32 +314,29 @@ namespace Netch.Forms
|
||||
|
||||
#region Options
|
||||
|
||||
private void CheckForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
private async void CheckForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Task.Run(() =>
|
||||
void OnNewVersionNotFound(object? o, EventArgs? args)
|
||||
{
|
||||
void OnNewVersionNotFound(object? o, EventArgs? args)
|
||||
{
|
||||
NotifyTip(i18N.Translate("Already latest version"));
|
||||
}
|
||||
NotifyTip(i18N.Translate("Already latest version"));
|
||||
}
|
||||
|
||||
void OnNewVersionFoundFailed(object? o, EventArgs? args)
|
||||
{
|
||||
NotifyTip(i18N.Translate("New version found failed"), info: false);
|
||||
}
|
||||
void OnNewVersionFoundFailed(object? o, EventArgs? args)
|
||||
{
|
||||
NotifyTip(i18N.Translate("New version found failed"), info: false);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
UpdateChecker.NewVersionNotFound += OnNewVersionNotFound;
|
||||
UpdateChecker.NewVersionFoundFailed += OnNewVersionFoundFailed;
|
||||
CheckUpdate();
|
||||
}
|
||||
finally
|
||||
{
|
||||
UpdateChecker.NewVersionNotFound -= OnNewVersionNotFound;
|
||||
UpdateChecker.NewVersionFoundFailed -= OnNewVersionFoundFailed;
|
||||
}
|
||||
});
|
||||
try
|
||||
{
|
||||
UpdateChecker.NewVersionNotFound += OnNewVersionNotFound;
|
||||
UpdateChecker.NewVersionFoundFailed += OnNewVersionFoundFailed;
|
||||
await CheckUpdate();
|
||||
}
|
||||
finally
|
||||
{
|
||||
UpdateChecker.NewVersionNotFound -= OnNewVersionNotFound;
|
||||
UpdateChecker.NewVersionFoundFailed -= OnNewVersionFoundFailed;
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
@@ -368,11 +372,10 @@ namespace Netch.Forms
|
||||
StatusText(i18N.TranslateFormat("Uninstalling {0}", "NF Service"));
|
||||
try
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
if (NFController.UninstallDriver())
|
||||
NotifyTip(i18N.TranslateFormat("{0} has been uninstalled", "NF Service"));
|
||||
});
|
||||
var task = Task.Run(NFController.UninstallDriver);
|
||||
|
||||
if (await task)
|
||||
NotifyTip(i18N.TranslateFormat("{0} has been uninstalled", "NF Service"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -386,6 +389,13 @@ namespace Netch.Forms
|
||||
Firewall.RemoveNetchFwRules();
|
||||
}
|
||||
|
||||
private void ShowHideConsoleToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
var windowStyles = (User32.WindowStyles)User32.GetWindowLong(Netch.ConsoleHwnd, User32.WindowLongFlags.GWL_STYLE);
|
||||
var visible = windowStyles.HasFlag(User32.WindowStyles.WS_VISIBLE);
|
||||
User32.ShowWindow(Netch.ConsoleHwnd, visible ? ShowWindowCommand.SW_HIDE : ShowWindowCommand.SW_SHOWNOACTIVATE);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@@ -419,21 +429,59 @@ namespace Netch.Forms
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Run(() =>
|
||||
var progress = new Progress<int>();
|
||||
progress.ProgressChanged += (_, percentage) => { NewVersionLabel.Text = $"{percentage}%"; };
|
||||
|
||||
string downloadDirectory = Path.Combine(Global.NetchDir, "data");
|
||||
|
||||
var (updateFileName, sha256) = UpdateChecker.GetLatestUpdateFileNameAndHash();
|
||||
var updateFileUrl = UpdateChecker.LatestRelease.assets[0].browser_download_url!;
|
||||
|
||||
var updateFileFullName = Path.Combine(downloadDirectory, updateFileName);
|
||||
var updater = new Updater(updateFileFullName, Global.NetchDir);
|
||||
|
||||
var downloaded = false;
|
||||
if (File.Exists(updateFileFullName))
|
||||
if (Utils.Utils.SHA256CheckSum(updateFileFullName) == sha256)
|
||||
downloaded = true;
|
||||
else
|
||||
File.Delete(updateFileFullName);
|
||||
|
||||
if (!downloaded)
|
||||
{
|
||||
Updater.Updater.DownloadAndUpdate(Path.Combine(Global.NetchDir, "data"),
|
||||
Global.NetchDir,
|
||||
(_, args) => BeginInvoke(new Action(() => NewVersionLabel.Text = $"{args.ProgressPercentage}%")));
|
||||
});
|
||||
try
|
||||
{
|
||||
await WebUtil.DownloadFileAsync(updateFileUrl, updateFileFullName, progress);
|
||||
}
|
||||
catch (Exception e1)
|
||||
{
|
||||
Log.Warning(e1, "Download Update File Failed");
|
||||
throw new MessageException($"Download Update File Failed: {e1.Message}");
|
||||
}
|
||||
|
||||
if (Utils.Utils.SHA256CheckSum(updateFileFullName) != sha256)
|
||||
throw new MessageException(i18N.Translate("The downloaded file has the wrong hash"));
|
||||
}
|
||||
|
||||
ModeHelper.SuspendWatcher = true;
|
||||
await Stop();
|
||||
await Configuration.SaveAsync();
|
||||
|
||||
// Update
|
||||
await Task.Run(updater.ApplyUpdate);
|
||||
|
||||
// release mutex, exit
|
||||
Netch.SingleInstance.Dispose();
|
||||
Process.Start(Global.NetchExecutable);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
catch (MessageException exception)
|
||||
{
|
||||
NotifyTip(exception.Message, info: false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (exception is not MessageException)
|
||||
{
|
||||
Global.Logger.Error($"更新失败: {exception}");
|
||||
Global.Logger.ShowLog();
|
||||
}
|
||||
|
||||
Log.Error(exception, "更新未处理异常");
|
||||
NotifyTip(exception.Message, info: false);
|
||||
}
|
||||
finally
|
||||
@@ -463,21 +511,20 @@ namespace Netch.Forms
|
||||
{
|
||||
if (!IsWaiting())
|
||||
{
|
||||
// 停止
|
||||
await StopAsyncCore();
|
||||
await StopCore();
|
||||
return;
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
await Configuration.SaveAsync();
|
||||
|
||||
// 服务器、模式 需选择
|
||||
if (!(ServerComboBox.SelectedItem is Server server))
|
||||
if (ServerComboBox.SelectedItem is not Server server)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(ModeComboBox.SelectedItem is Models.Mode mode))
|
||||
if (ModeComboBox.SelectedItem is not Mode mode)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||||
return;
|
||||
@@ -499,34 +546,29 @@ namespace Netch.Forms
|
||||
|
||||
State = State.Started;
|
||||
|
||||
_ = Task.Run(Bandwidth.NetTraffic);
|
||||
_ = Task.Run(NatTest);
|
||||
Task.Run(Bandwidth.NetTraffic).Forget();
|
||||
Task.Run(NatTest).Forget();
|
||||
|
||||
if (Global.Settings.MinimizeWhenStarted)
|
||||
Minimize();
|
||||
|
||||
// 自动检测延迟
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
while (State == State.Started)
|
||||
Task.Run(() =>
|
||||
{
|
||||
bool StartedPingEnabled()
|
||||
{
|
||||
return Global.Settings.StartedPingInterval >= 0;
|
||||
}
|
||||
while (State == State.Started)
|
||||
if (Global.Settings.StartedPingInterval >= 0)
|
||||
{
|
||||
server.Test();
|
||||
ServerComboBox.Refresh();
|
||||
|
||||
if (StartedPingEnabled())
|
||||
{
|
||||
server.Test();
|
||||
ServerComboBox.Refresh();
|
||||
}
|
||||
|
||||
if (StartedPingEnabled())
|
||||
Thread.Sleep(Global.Settings.StartedPingInterval * 1000);
|
||||
else
|
||||
Thread.Sleep(5000);
|
||||
}
|
||||
});
|
||||
Thread.Sleep(Global.Settings.StartedPingInterval * 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
}
|
||||
})
|
||||
.Forget();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -564,11 +606,11 @@ namespace Netch.Forms
|
||||
private void LoadServers()
|
||||
{
|
||||
ServerComboBox.Items.Clear();
|
||||
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
|
||||
ServerComboBox.Items.AddRange(Global.Settings.Server.Cast<object>().ToArray());
|
||||
SelectLastServer();
|
||||
}
|
||||
|
||||
public void SelectLastServer()
|
||||
private void SelectLastServer()
|
||||
{
|
||||
// 如果值合法,选中该位置
|
||||
if (Global.Settings.ServerComboBoxSelectedIndex > 0 && Global.Settings.ServerComboBoxSelectedIndex < ServerComboBox.Items.Count)
|
||||
@@ -585,7 +627,7 @@ namespace Netch.Forms
|
||||
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
|
||||
}
|
||||
|
||||
private void EditServerPictureBox_Click(object sender, EventArgs e)
|
||||
private async void EditServerPictureBox_Click(object sender, EventArgs e)
|
||||
{
|
||||
// 当前ServerComboBox中至少有一项
|
||||
if (!(ServerComboBox.SelectedItem is Server server))
|
||||
@@ -600,34 +642,36 @@ namespace Netch.Forms
|
||||
Hide();
|
||||
ServerHelper.GetUtilByTypeName(server.Type).Edit(server);
|
||||
LoadServers();
|
||||
Configuration.Save();
|
||||
await Configuration.SaveAsync();
|
||||
Show();
|
||||
}
|
||||
|
||||
private void SpeedPictureBox_Click(object sender, EventArgs e)
|
||||
{
|
||||
void Enable()
|
||||
{
|
||||
ServerComboBox.Refresh();
|
||||
Enabled = true;
|
||||
StatusText();
|
||||
}
|
||||
|
||||
Enabled = false;
|
||||
StatusText(i18N.Translate("Testing"));
|
||||
|
||||
if (!IsWaiting() || ModifierKeys == Keys.Control)
|
||||
{
|
||||
(ServerComboBox.SelectedItem as Server)?.Test();
|
||||
ServerComboBox.Refresh();
|
||||
Enabled = true;
|
||||
StatusText();
|
||||
Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerHelper.DelayTestHelper.TestDelayFinished += OnTestDelayFinished;
|
||||
_ = Task.Run(ServerHelper.DelayTestHelper.TestAllDelay);
|
||||
Task.Run(ServerHelper.DelayTestHelper.TestAllDelay).Forget();
|
||||
|
||||
void OnTestDelayFinished(object? o1, EventArgs? e1)
|
||||
{
|
||||
Refresh();
|
||||
|
||||
ServerHelper.DelayTestHelper.TestDelayFinished -= OnTestDelayFinished;
|
||||
Enabled = true;
|
||||
StatusText();
|
||||
Enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -680,13 +724,19 @@ namespace Netch.Forms
|
||||
|
||||
public void LoadModes()
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(new Action(LoadModes));
|
||||
return;
|
||||
}
|
||||
|
||||
ModeComboBox.Items.Clear();
|
||||
ModeComboBox.Items.AddRange(Global.Modes.Cast<object>().ToArray());
|
||||
ModeComboBox.Tag = null;
|
||||
SelectLastMode();
|
||||
}
|
||||
|
||||
public void SelectLastMode()
|
||||
private void SelectLastMode()
|
||||
{
|
||||
// 如果值合法,选中该位置
|
||||
if (Global.Settings.ModeComboBoxSelectedIndex > 0 && Global.Settings.ModeComboBoxSelectedIndex < ModeComboBox.Items.Count)
|
||||
@@ -702,7 +752,7 @@ namespace Netch.Forms
|
||||
{
|
||||
try
|
||||
{
|
||||
Global.Settings.ModeComboBoxSelectedIndex = Global.Modes.IndexOf((Models.Mode)ModeComboBox.SelectedItem);
|
||||
Global.Settings.ModeComboBoxSelectedIndex = Global.Modes.IndexOf((Mode)ModeComboBox.SelectedItem);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -719,7 +769,7 @@ namespace Netch.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
var mode = (Models.Mode)ModeComboBox.SelectedItem;
|
||||
var mode = (Mode)ModeComboBox.SelectedItem;
|
||||
if (ModifierKeys == Keys.Control)
|
||||
{
|
||||
Utils.Utils.Open(ModeHelper.GetFullPath(mode.RelativePath!));
|
||||
@@ -730,13 +780,13 @@ namespace Netch.Forms
|
||||
{
|
||||
case ModeType.Process:
|
||||
Hide();
|
||||
new Process(mode).ShowDialog();
|
||||
new ProcessForm(mode).ShowDialog();
|
||||
Show();
|
||||
break;
|
||||
case ModeType.ProxyRuleIPs:
|
||||
case ModeType.BypassRuleIPs:
|
||||
Hide();
|
||||
new Route(mode).ShowDialog();
|
||||
new RouteForm(mode).ShowDialog();
|
||||
Show();
|
||||
break;
|
||||
default:
|
||||
@@ -754,7 +804,7 @@ namespace Netch.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
ModeHelper.Delete((Models.Mode)ModeComboBox.SelectedItem);
|
||||
ModeHelper.Delete((Mode)ModeComboBox.SelectedItem);
|
||||
SelectLastMode();
|
||||
}
|
||||
|
||||
@@ -832,7 +882,7 @@ namespace Netch.Forms
|
||||
ProfileNameText.Text = profile.ProfileName;
|
||||
|
||||
var server = ServerComboBox.Items.Cast<Server>().FirstOrDefault(s => s.Remark.Equals(profile.ServerRemark));
|
||||
var mode = ModeComboBox.Items.Cast<Models.Mode>().FirstOrDefault(m => m.Remark.Equals(profile.ModeRemark));
|
||||
var mode = ModeComboBox.Items.Cast<Mode>().FirstOrDefault(m => m.Remark.Equals(profile.ModeRemark));
|
||||
|
||||
if (server == null)
|
||||
throw new Exception("Server not found.");
|
||||
@@ -847,7 +897,7 @@ namespace Netch.Forms
|
||||
private Profile CreateProfileAtIndex(int index)
|
||||
{
|
||||
var server = (Server)ServerComboBox.SelectedItem;
|
||||
var mode = (Models.Mode)ModeComboBox.SelectedItem;
|
||||
var mode = (Mode)ModeComboBox.SelectedItem;
|
||||
var name = ProfileNameText.Text;
|
||||
|
||||
Profile? profile;
|
||||
@@ -1003,25 +1053,19 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StopAsyncCore()
|
||||
{
|
||||
State = State.Stopping;
|
||||
await MainController.StopAsync();
|
||||
State = State.Stopped;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
public async Task Stop()
|
||||
{
|
||||
if (IsWaiting())
|
||||
return;
|
||||
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(new Action(Stop));
|
||||
return;
|
||||
}
|
||||
await StopCore();
|
||||
}
|
||||
|
||||
StopAsyncCore().Wait();
|
||||
private async Task StopCore()
|
||||
{
|
||||
State = State.Stopping;
|
||||
await MainController.StopAsync();
|
||||
State = State.Stopped;
|
||||
}
|
||||
|
||||
private bool IsWaiting()
|
||||
@@ -1121,28 +1165,32 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
private void NatTypeStatusLabel_Click(object sender, EventArgs e)
|
||||
private async void NatTypeStatusLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_state == State.Started && NttTested)
|
||||
NatTest();
|
||||
if (_state == State.Started && !Monitor.IsEntered(_natTestLock))
|
||||
await NatTest();
|
||||
}
|
||||
|
||||
private static bool NttTested;
|
||||
private bool _natTestLock = true;
|
||||
|
||||
/// <summary>
|
||||
/// 测试 NAT
|
||||
/// </summary>
|
||||
public void NatTest()
|
||||
private async Task NatTest()
|
||||
{
|
||||
if (!MainController.Mode!.TestNatRequired())
|
||||
return;
|
||||
|
||||
NttTested = false;
|
||||
Task.Run(() =>
|
||||
{
|
||||
NatTypeStatusText(i18N.Translate("Starting NatTester"));
|
||||
if (!_natTestLock)
|
||||
return;
|
||||
|
||||
var (result, localEnd, publicEnd) = MainController.NTTController.Start().Result;
|
||||
_natTestLock = false;
|
||||
|
||||
try
|
||||
{
|
||||
NatTypeStatusText(i18N.Translate("Testing NAT"));
|
||||
|
||||
var (result, _, publicEnd) = await MainController.NTTController.Start();
|
||||
|
||||
if (!string.IsNullOrEmpty(publicEnd))
|
||||
{
|
||||
@@ -1153,9 +1201,11 @@ namespace Netch.Forms
|
||||
{
|
||||
NatTypeStatusText(result ?? "Error");
|
||||
}
|
||||
|
||||
NttTested = true;
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
_natTestLock = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1176,7 +1226,7 @@ namespace Netch.Forms
|
||||
if (!IsWaiting())
|
||||
{
|
||||
_resumeFlag = true;
|
||||
Global.Logger.Info("操作系统即将挂起,自动停止");
|
||||
Log.Information("操作系统即将挂起,自动停止");
|
||||
ControlButton_Click(null, null);
|
||||
}
|
||||
|
||||
@@ -1185,7 +1235,7 @@ namespace Netch.Forms
|
||||
if (_resumeFlag)
|
||||
{
|
||||
_resumeFlag = false;
|
||||
Global.Logger.Info("操作系统即将从挂起状态继续,自动重启");
|
||||
Log.Information("操作系统即将从挂起状态继续,自动重启");
|
||||
ControlButton_Click(null, null);
|
||||
}
|
||||
|
||||
@@ -1225,16 +1275,13 @@ namespace Netch.Forms
|
||||
Hide();
|
||||
|
||||
if (saveConfiguration)
|
||||
{
|
||||
Configuration.Save();
|
||||
}
|
||||
await Configuration.SaveAsync();
|
||||
|
||||
foreach (var file in new[] { "data\\last.json", "data\\privoxy.conf" })
|
||||
foreach (var file in new[] { Constants.TempConfig, Constants.TempRouteFile })
|
||||
if (File.Exists(file))
|
||||
File.Delete(file);
|
||||
|
||||
if (!IsWaiting())
|
||||
await StopAsyncCore();
|
||||
await Stop();
|
||||
|
||||
Dispose();
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
@@ -1264,12 +1311,12 @@ namespace Netch.Forms
|
||||
|
||||
#region Updater
|
||||
|
||||
private void CheckUpdate()
|
||||
private async Task CheckUpdate()
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateChecker.NewVersionFound += OnUpdateCheckerOnNewVersionFound;
|
||||
UpdateChecker.Check(Global.Settings.CheckBetaUpdate).Wait();
|
||||
await UpdateChecker.Check(Global.Settings.CheckBetaUpdate);
|
||||
if (Flags.AlwaysShowNewVersionFound)
|
||||
OnUpdateCheckerOnNewVersionFound(null!, null!);
|
||||
}
|
||||
@@ -1337,14 +1384,18 @@ namespace Netch.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
if (WindowState == FormWindowState.Minimized)
|
||||
{
|
||||
Visible = true;
|
||||
ShowInTaskbar = true; // 显示在系统任务栏
|
||||
WindowState = FormWindowState.Normal; // 还原窗体
|
||||
}
|
||||
var forms = Application.OpenForms.Cast<Form>().ToList();
|
||||
var anyWindowOpened = forms.Any(f => f is not (MainForm or LogForm));
|
||||
|
||||
Activate();
|
||||
forms.ForEach(f =>
|
||||
{
|
||||
if (anyWindowOpened && f is MainForm or LogForm)
|
||||
return;
|
||||
|
||||
f.Show();
|
||||
f.WindowState = FormWindowState.Normal;
|
||||
f.Activate();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1357,14 +1408,7 @@ namespace Netch.Forms
|
||||
|
||||
private void NotifyIcon_MouseDoubleClick(object? sender, MouseEventArgs? e)
|
||||
{
|
||||
if (WindowState == FormWindowState.Minimized)
|
||||
{
|
||||
Visible = true;
|
||||
ShowInTaskbar = true; //显示在系统任务栏
|
||||
WindowState = FormWindowState.Normal; //还原窗体
|
||||
}
|
||||
|
||||
Activate();
|
||||
ShowMainFormToolStripButton.PerformClick();
|
||||
}
|
||||
|
||||
public void NotifyTip(string text, int timeout = 0, bool info = true)
|
||||
@@ -1384,7 +1428,7 @@ namespace Netch.Forms
|
||||
|
||||
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
|
||||
{
|
||||
if (!(sender is ComboBox cbx))
|
||||
if (sender is not ComboBox cbx)
|
||||
return;
|
||||
|
||||
// 绘制背景颜色
|
||||
@@ -1416,7 +1460,7 @@ namespace Netch.Forms
|
||||
|
||||
break;
|
||||
}
|
||||
case Models.Mode item:
|
||||
case Mode item:
|
||||
{
|
||||
// 绘制 模式Box 底色
|
||||
e.Graphics.FillRectangle(Brushes.Gray, _numberBoxX, e.Bounds.Y, _numberBoxWidth, e.Bounds.Height);
|
||||
|
||||
@@ -57,4 +57,16 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="MenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="StatusStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>130, 17</value>
|
||||
</metadata>
|
||||
<metadata name="NotifyIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>246, 17</value>
|
||||
</metadata>
|
||||
<metadata name="NotifyMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>359, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Netch.Forms.Mode
|
||||
namespace Netch.Forms.ModeForms
|
||||
{
|
||||
public static class ModeEditorUtils
|
||||
{
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Netch.Forms.Mode
|
||||
namespace Netch.Forms.ModeForms
|
||||
{
|
||||
partial class Process
|
||||
partial class ProcessForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
@@ -180,7 +180,7 @@ namespace Netch.Forms.Mode
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "Process";
|
||||
this.Name = "ProcessForm";
|
||||
this.Padding = new System.Windows.Forms.Padding(12, 5, 12, 5);
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Create Process Mode";
|
||||
@@ -10,20 +10,20 @@ using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Enums;
|
||||
|
||||
namespace Netch.Forms.Mode
|
||||
namespace Netch.Forms.ModeForms
|
||||
{
|
||||
public partial class Process : Form
|
||||
public partial class ProcessForm : Form
|
||||
{
|
||||
/// <summary>
|
||||
/// 被编辑的模式
|
||||
/// </summary>
|
||||
private readonly Models.Mode? _mode;
|
||||
private readonly Mode? _mode;
|
||||
|
||||
/// <summary>
|
||||
/// 编辑模式
|
||||
/// </summary>
|
||||
/// <param name="mode">模式</param>
|
||||
public Process(Models.Mode? mode = null)
|
||||
public ProcessForm(Mode? mode = null)
|
||||
{
|
||||
if (mode != null && mode.Type is not ModeType.Process)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
@@ -133,7 +133,7 @@ namespace Netch.Forms.Mode
|
||||
return;
|
||||
}
|
||||
|
||||
var mode = new Models.Mode(fullName)
|
||||
var mode = new Mode(fullName)
|
||||
{
|
||||
Type = ModeType.Process,
|
||||
Remark = RemarkTextBox.Text
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.ComponentModel;
|
||||
using Netch.Properties;
|
||||
|
||||
namespace Netch.Forms.Mode
|
||||
namespace Netch.Forms.ModeForms
|
||||
{
|
||||
partial class Route
|
||||
partial class RouteForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
@@ -166,7 +166,7 @@ namespace Netch.Forms.Mode
|
||||
this.ClientSize = new System.Drawing.Size(356, 419);
|
||||
this.Controls.Add(this.ConfigurationGroupBox);
|
||||
this.Controls.Add(this.ControlButton);
|
||||
this.Name = "Route";
|
||||
this.Name = "RouteForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Create Route Table Rule";
|
||||
this.Load += new System.EventHandler(this.Route_Load);
|
||||
@@ -6,16 +6,16 @@ using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Enums;
|
||||
|
||||
namespace Netch.Forms.Mode
|
||||
namespace Netch.Forms.ModeForms
|
||||
{
|
||||
public partial class Route : Form
|
||||
public partial class RouteForm : Form
|
||||
{
|
||||
private readonly TagItem<ModeType>[] _items =
|
||||
{ new(ModeType.ProxyRuleIPs, "Proxy Rule IPs"), new(ModeType.BypassRuleIPs, "Bypass Rule IPs") };
|
||||
|
||||
private readonly Models.Mode? _mode;
|
||||
private readonly Mode? _mode;
|
||||
|
||||
public Route(Models.Mode? mode = null)
|
||||
public RouteForm(Mode? mode = null)
|
||||
{
|
||||
if (mode != null && mode.Type is not (ModeType.ProxyRuleIPs or ModeType.BypassRuleIPs))
|
||||
throw new ArgumentOutOfRangeException();
|
||||
@@ -79,7 +79,7 @@ namespace Netch.Forms.Mode
|
||||
return;
|
||||
}
|
||||
|
||||
var mode = new Models.Mode(fullName)
|
||||
var mode = new Mode(fullName)
|
||||
{
|
||||
Type = (ModeType)comboBox1.SelectedValue,
|
||||
Remark = RemarkTextBox.Text
|
||||
@@ -1,13 +1,13 @@
|
||||
#nullable disable
|
||||
using Netch.Models;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Models;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Forms
|
||||
{
|
||||
@@ -74,6 +74,8 @@ namespace Netch.Forms
|
||||
AddSaveButton();
|
||||
i18N.TranslateForm(this);
|
||||
|
||||
ConfigurationGroupBox.Enabled = string.IsNullOrEmpty(Server.Remark);
|
||||
|
||||
ConfigurationGroupBox.ResumeLayout(false);
|
||||
ConfigurationGroupBox.PerformLayout();
|
||||
ResumeLayout(false);
|
||||
|
||||
246
Netch/Forms/SettingForm.Designer.cs
generated
246
Netch/Forms/SettingForm.Designer.cs
generated
@@ -54,13 +54,13 @@ namespace Netch.Forms
|
||||
this.LanguageLabel = new System.Windows.Forms.Label();
|
||||
this.LanguageComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.NFTabPage = new System.Windows.Forms.TabPage();
|
||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
this.ProcessProxyProtocolLabel = new System.Windows.Forms.Label();
|
||||
this.ProcessProxyProtocolComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.ProcessFilterProtocolLabel = new System.Windows.Forms.Label();
|
||||
this.ProcessFilterProtocolComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.DNSHijackCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.DNSHijackHostTextBox = new System.Windows.Forms.TextBox();
|
||||
this.ICMPHijackCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.ICMPHijackHostTextBox = new System.Windows.Forms.TextBox();
|
||||
this.FilterICMPCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.ICMPDelayLabel = new System.Windows.Forms.Label();
|
||||
this.ICMPDelayTextBox = new System.Windows.Forms.TextBox();
|
||||
this.RedirectorSSCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.ChildProcessHandleCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.WinTUNTabPage = new System.Windows.Forms.TabPage();
|
||||
@@ -104,19 +104,18 @@ namespace Netch.Forms
|
||||
this.CheckBetaUpdateCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.UpdateServersWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.AioDNSTabPage = new System.Windows.Forms.TabPage();
|
||||
this.AioDNSRuleRuleLabel = new System.Windows.Forms.Label();
|
||||
this.AioDNSRulePathTextBox = new System.Windows.Forms.TextBox();
|
||||
this.ChinaDNSLabel = new System.Windows.Forms.Label();
|
||||
this.ChinaDNSTextBox = new System.Windows.Forms.TextBox();
|
||||
this.OtherDNSLabel = new System.Windows.Forms.Label();
|
||||
this.OtherDNSTextBox = new System.Windows.Forms.TextBox();
|
||||
this.AioDNSListenPortLabel = new System.Windows.Forms.Label();
|
||||
this.AioDNSListenPortTextBox = new System.Windows.Forms.TextBox();
|
||||
this.ControlButton = new System.Windows.Forms.Button();
|
||||
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.TabControl.SuspendLayout();
|
||||
this.GeneralTabPage.SuspendLayout();
|
||||
this.PortGroupBox.SuspendLayout();
|
||||
this.NFTabPage.SuspendLayout();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.WinTUNTabPage.SuspendLayout();
|
||||
this.WinTUNGroupBox.SuspendLayout();
|
||||
this.v2rayTabPage.SuspendLayout();
|
||||
@@ -175,7 +174,7 @@ namespace Netch.Forms
|
||||
this.PortGroupBox.Controls.Add(this.AllowDevicesCheckBox);
|
||||
this.PortGroupBox.Location = new System.Drawing.Point(8, 6);
|
||||
this.PortGroupBox.Name = "PortGroupBox";
|
||||
this.PortGroupBox.Size = new System.Drawing.Size(241, 140);
|
||||
this.PortGroupBox.Size = new System.Drawing.Size(241, 115);
|
||||
this.PortGroupBox.TabIndex = 0;
|
||||
this.PortGroupBox.TabStop = false;
|
||||
this.PortGroupBox.Text = "Local Port";
|
||||
@@ -217,10 +216,10 @@ namespace Netch.Forms
|
||||
// AllowDevicesCheckBox
|
||||
//
|
||||
this.AllowDevicesCheckBox.AutoSize = true;
|
||||
this.AllowDevicesCheckBox.Location = new System.Drawing.Point(6, 107);
|
||||
this.AllowDevicesCheckBox.Location = new System.Drawing.Point(6, 84);
|
||||
this.AllowDevicesCheckBox.Name = "AllowDevicesCheckBox";
|
||||
this.AllowDevicesCheckBox.Size = new System.Drawing.Size(206, 21);
|
||||
this.AllowDevicesCheckBox.TabIndex = 6;
|
||||
this.AllowDevicesCheckBox.TabIndex = 4;
|
||||
this.AllowDevicesCheckBox.Text = "Allow other Devices to connect";
|
||||
this.AllowDevicesCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
this.AllowDevicesCheckBox.UseVisualStyleBackColor = true;
|
||||
@@ -240,9 +239,9 @@ namespace Netch.Forms
|
||||
this.ServerPingTypeLabel.AutoSize = true;
|
||||
this.ServerPingTypeLabel.Location = new System.Drawing.Point(267, 44);
|
||||
this.ServerPingTypeLabel.Name = "ServerPingTypeLabel";
|
||||
this.ServerPingTypeLabel.Size = new System.Drawing.Size(98, 17);
|
||||
this.ServerPingTypeLabel.Size = new System.Drawing.Size(86, 17);
|
||||
this.ServerPingTypeLabel.TabIndex = 2;
|
||||
this.ServerPingTypeLabel.Text = "ServerPingType";
|
||||
this.ServerPingTypeLabel.Text = "Ping Protocol";
|
||||
//
|
||||
// ICMPingRadioBtn
|
||||
//
|
||||
@@ -269,24 +268,24 @@ namespace Netch.Forms
|
||||
// ProfileCountLabel
|
||||
//
|
||||
this.ProfileCountLabel.AutoSize = true;
|
||||
this.ProfileCountLabel.Location = new System.Drawing.Point(12, 160);
|
||||
this.ProfileCountLabel.Location = new System.Drawing.Point(15, 140);
|
||||
this.ProfileCountLabel.Name = "ProfileCountLabel";
|
||||
this.ProfileCountLabel.Size = new System.Drawing.Size(79, 17);
|
||||
this.ProfileCountLabel.Size = new System.Drawing.Size(83, 17);
|
||||
this.ProfileCountLabel.TabIndex = 5;
|
||||
this.ProfileCountLabel.Text = "ProfileCount";
|
||||
this.ProfileCountLabel.Text = "Profile Count";
|
||||
//
|
||||
// ProfileCountTextBox
|
||||
//
|
||||
this.ProfileCountTextBox.Location = new System.Drawing.Point(120, 157);
|
||||
this.ProfileCountTextBox.Location = new System.Drawing.Point(182, 137);
|
||||
this.ProfileCountTextBox.Name = "ProfileCountTextBox";
|
||||
this.ProfileCountTextBox.Size = new System.Drawing.Size(90, 23);
|
||||
this.ProfileCountTextBox.Size = new System.Drawing.Size(70, 23);
|
||||
this.ProfileCountTextBox.TabIndex = 6;
|
||||
this.ProfileCountTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// DetectionTickLabel
|
||||
//
|
||||
this.DetectionTickLabel.AutoSize = true;
|
||||
this.DetectionTickLabel.Location = new System.Drawing.Point(225, 160);
|
||||
this.DetectionTickLabel.Location = new System.Drawing.Point(15, 170);
|
||||
this.DetectionTickLabel.Name = "DetectionTickLabel";
|
||||
this.DetectionTickLabel.Size = new System.Drawing.Size(117, 17);
|
||||
this.DetectionTickLabel.TabIndex = 7;
|
||||
@@ -294,33 +293,33 @@ namespace Netch.Forms
|
||||
//
|
||||
// DetectionTickTextBox
|
||||
//
|
||||
this.DetectionTickTextBox.Location = new System.Drawing.Point(366, 157);
|
||||
this.DetectionTickTextBox.Location = new System.Drawing.Point(182, 167);
|
||||
this.DetectionTickTextBox.Name = "DetectionTickTextBox";
|
||||
this.DetectionTickTextBox.Size = new System.Drawing.Size(68, 23);
|
||||
this.DetectionTickTextBox.Size = new System.Drawing.Size(70, 23);
|
||||
this.DetectionTickTextBox.TabIndex = 8;
|
||||
this.DetectionTickTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// StartedPingLabel
|
||||
//
|
||||
this.StartedPingLabel.AutoSize = true;
|
||||
this.StartedPingLabel.Location = new System.Drawing.Point(12, 187);
|
||||
this.StartedPingLabel.Location = new System.Drawing.Point(15, 200);
|
||||
this.StartedPingLabel.Name = "StartedPingLabel";
|
||||
this.StartedPingLabel.Size = new System.Drawing.Size(126, 17);
|
||||
this.StartedPingLabel.Size = new System.Drawing.Size(153, 17);
|
||||
this.StartedPingLabel.TabIndex = 9;
|
||||
this.StartedPingLabel.Text = "Delay test after start";
|
||||
this.StartedPingLabel.Text = "Delay test after start(sec)";
|
||||
//
|
||||
// StartedPingIntervalTextBox
|
||||
//
|
||||
this.StartedPingIntervalTextBox.Location = new System.Drawing.Point(177, 184);
|
||||
this.StartedPingIntervalTextBox.Location = new System.Drawing.Point(182, 197);
|
||||
this.StartedPingIntervalTextBox.Name = "StartedPingIntervalTextBox";
|
||||
this.StartedPingIntervalTextBox.Size = new System.Drawing.Size(68, 23);
|
||||
this.StartedPingIntervalTextBox.Size = new System.Drawing.Size(70, 23);
|
||||
this.StartedPingIntervalTextBox.TabIndex = 10;
|
||||
this.StartedPingIntervalTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// STUNServerLabel
|
||||
//
|
||||
this.STUNServerLabel.AutoSize = true;
|
||||
this.STUNServerLabel.Location = new System.Drawing.Point(12, 216);
|
||||
this.STUNServerLabel.Location = new System.Drawing.Point(15, 230);
|
||||
this.STUNServerLabel.Name = "STUNServerLabel";
|
||||
this.STUNServerLabel.Size = new System.Drawing.Size(82, 17);
|
||||
this.STUNServerLabel.TabIndex = 11;
|
||||
@@ -329,33 +328,39 @@ namespace Netch.Forms
|
||||
// STUN_ServerComboBox
|
||||
//
|
||||
this.STUN_ServerComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
|
||||
this.STUN_ServerComboBox.Location = new System.Drawing.Point(120, 213);
|
||||
this.STUN_ServerComboBox.Location = new System.Drawing.Point(182, 227);
|
||||
this.STUN_ServerComboBox.Name = "STUN_ServerComboBox";
|
||||
this.STUN_ServerComboBox.Size = new System.Drawing.Size(314, 25);
|
||||
this.STUN_ServerComboBox.Size = new System.Drawing.Size(264, 25);
|
||||
this.STUN_ServerComboBox.TabIndex = 12;
|
||||
//
|
||||
// LanguageLabel
|
||||
//
|
||||
this.LanguageLabel.AutoSize = true;
|
||||
this.LanguageLabel.Location = new System.Drawing.Point(12, 254);
|
||||
this.LanguageLabel.Location = new System.Drawing.Point(15, 260);
|
||||
this.LanguageLabel.Name = "LanguageLabel";
|
||||
this.LanguageLabel.Size = new System.Drawing.Size(65, 17);
|
||||
this.LanguageLabel.TabIndex = 15;
|
||||
this.LanguageLabel.TabIndex = 13;
|
||||
this.LanguageLabel.Text = "Language";
|
||||
//
|
||||
// LanguageComboBox
|
||||
//
|
||||
this.LanguageComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.LanguageComboBox.FormattingEnabled = true;
|
||||
this.LanguageComboBox.Location = new System.Drawing.Point(120, 251);
|
||||
this.LanguageComboBox.Location = new System.Drawing.Point(182, 257);
|
||||
this.LanguageComboBox.Name = "LanguageComboBox";
|
||||
this.LanguageComboBox.Size = new System.Drawing.Size(121, 25);
|
||||
this.LanguageComboBox.TabIndex = 16;
|
||||
this.LanguageComboBox.Size = new System.Drawing.Size(110, 25);
|
||||
this.LanguageComboBox.TabIndex = 14;
|
||||
//
|
||||
// NFTabPage
|
||||
//
|
||||
this.NFTabPage.BackColor = System.Drawing.SystemColors.ButtonFace;
|
||||
this.NFTabPage.Controls.Add(this.groupBox1);
|
||||
this.NFTabPage.Controls.Add(this.ProcessFilterProtocolLabel);
|
||||
this.NFTabPage.Controls.Add(this.ProcessFilterProtocolComboBox);
|
||||
this.NFTabPage.Controls.Add(this.DNSHijackCheckBox);
|
||||
this.NFTabPage.Controls.Add(this.DNSHijackHostTextBox);
|
||||
this.NFTabPage.Controls.Add(this.FilterICMPCheckBox);
|
||||
this.NFTabPage.Controls.Add(this.ICMPDelayLabel);
|
||||
this.NFTabPage.Controls.Add(this.ICMPDelayTextBox);
|
||||
this.NFTabPage.Controls.Add(this.RedirectorSSCheckBox);
|
||||
this.NFTabPage.Controls.Add(this.ChildProcessHandleCheckBox);
|
||||
this.NFTabPage.Location = new System.Drawing.Point(4, 29);
|
||||
@@ -365,42 +370,28 @@ namespace Netch.Forms
|
||||
this.NFTabPage.TabIndex = 1;
|
||||
this.NFTabPage.Text = "Process Mode";
|
||||
//
|
||||
// groupBox1
|
||||
// ProcessFilterProtocolLabel
|
||||
//
|
||||
this.groupBox1.Controls.Add(this.ProcessProxyProtocolLabel);
|
||||
this.groupBox1.Controls.Add(this.ProcessProxyProtocolComboBox);
|
||||
this.groupBox1.Controls.Add(this.DNSHijackCheckBox);
|
||||
this.groupBox1.Controls.Add(this.DNSHijackHostTextBox);
|
||||
this.groupBox1.Controls.Add(this.ICMPHijackCheckBox);
|
||||
this.groupBox1.Controls.Add(this.ICMPHijackHostTextBox);
|
||||
this.groupBox1.Location = new System.Drawing.Point(5, 6);
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.Size = new System.Drawing.Size(450, 117);
|
||||
this.groupBox1.TabIndex = 0;
|
||||
this.groupBox1.TabStop = false;
|
||||
this.ProcessFilterProtocolLabel.AutoSize = true;
|
||||
this.ProcessFilterProtocolLabel.Location = new System.Drawing.Point(30, 20);
|
||||
this.ProcessFilterProtocolLabel.Name = "ProcessFilterProtocolLabel";
|
||||
this.ProcessFilterProtocolLabel.Size = new System.Drawing.Size(89, 17);
|
||||
this.ProcessFilterProtocolLabel.TabIndex = 0;
|
||||
this.ProcessFilterProtocolLabel.Text = "Filter Protocol";
|
||||
//
|
||||
// ProcessProxyProtocolLabel
|
||||
// ProcessFilterProtocolComboBox
|
||||
//
|
||||
this.ProcessProxyProtocolLabel.AutoSize = true;
|
||||
this.ProcessProxyProtocolLabel.Location = new System.Drawing.Point(23, 21);
|
||||
this.ProcessProxyProtocolLabel.Name = "ProcessProxyProtocolLabel";
|
||||
this.ProcessProxyProtocolLabel.Size = new System.Drawing.Size(93, 17);
|
||||
this.ProcessProxyProtocolLabel.TabIndex = 0;
|
||||
this.ProcessProxyProtocolLabel.Text = "Proxy Protocol";
|
||||
//
|
||||
// ProcessProxyProtocolComboBox
|
||||
//
|
||||
this.ProcessProxyProtocolComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.ProcessProxyProtocolComboBox.FormattingEnabled = true;
|
||||
this.ProcessProxyProtocolComboBox.Location = new System.Drawing.Point(118, 16);
|
||||
this.ProcessProxyProtocolComboBox.Name = "ProcessProxyProtocolComboBox";
|
||||
this.ProcessProxyProtocolComboBox.Size = new System.Drawing.Size(191, 25);
|
||||
this.ProcessProxyProtocolComboBox.TabIndex = 1;
|
||||
this.ProcessFilterProtocolComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.ProcessFilterProtocolComboBox.FormattingEnabled = true;
|
||||
this.ProcessFilterProtocolComboBox.Location = new System.Drawing.Point(237, 17);
|
||||
this.ProcessFilterProtocolComboBox.Name = "ProcessFilterProtocolComboBox";
|
||||
this.ProcessFilterProtocolComboBox.Size = new System.Drawing.Size(98, 25);
|
||||
this.ProcessFilterProtocolComboBox.TabIndex = 1;
|
||||
//
|
||||
// DNSHijackCheckBox
|
||||
//
|
||||
this.DNSHijackCheckBox.AutoSize = true;
|
||||
this.DNSHijackCheckBox.Location = new System.Drawing.Point(6, 51);
|
||||
this.DNSHijackCheckBox.Location = new System.Drawing.Point(15, 50);
|
||||
this.DNSHijackCheckBox.Name = "DNSHijackCheckBox";
|
||||
this.DNSHijackCheckBox.Size = new System.Drawing.Size(196, 21);
|
||||
this.DNSHijackCheckBox.TabIndex = 2;
|
||||
@@ -410,51 +401,58 @@ namespace Netch.Forms
|
||||
// DNSHijackHostTextBox
|
||||
//
|
||||
this.DNSHijackHostTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.DNSHijackCheckBox, "Checked", true));
|
||||
this.DNSHijackHostTextBox.Location = new System.Drawing.Point(253, 46);
|
||||
this.DNSHijackHostTextBox.Location = new System.Drawing.Point(237, 48);
|
||||
this.DNSHijackHostTextBox.Name = "DNSHijackHostTextBox";
|
||||
this.DNSHijackHostTextBox.Size = new System.Drawing.Size(191, 23);
|
||||
this.DNSHijackHostTextBox.TabIndex = 4;
|
||||
this.DNSHijackHostTextBox.TabIndex = 3;
|
||||
this.DNSHijackHostTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// ICMPHijackCheckBox
|
||||
// FilterICMPCheckBox
|
||||
//
|
||||
this.ICMPHijackCheckBox.AutoSize = true;
|
||||
this.ICMPHijackCheckBox.Enabled = false;
|
||||
this.ICMPHijackCheckBox.Location = new System.Drawing.Point(6, 81);
|
||||
this.ICMPHijackCheckBox.Name = "ICMPHijackCheckBox";
|
||||
this.ICMPHijackCheckBox.Size = new System.Drawing.Size(139, 21);
|
||||
this.ICMPHijackCheckBox.TabIndex = 5;
|
||||
this.ICMPHijackCheckBox.Text = "Global ICMP Hijack";
|
||||
this.ICMPHijackCheckBox.UseVisualStyleBackColor = true;
|
||||
this.FilterICMPCheckBox.AutoSize = true;
|
||||
this.FilterICMPCheckBox.Location = new System.Drawing.Point(13, 80);
|
||||
this.FilterICMPCheckBox.Name = "FilterICMPCheckBox";
|
||||
this.FilterICMPCheckBox.Size = new System.Drawing.Size(90, 21);
|
||||
this.FilterICMPCheckBox.TabIndex = 4;
|
||||
this.FilterICMPCheckBox.Text = "Filter ICMP";
|
||||
this.FilterICMPCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// ICMPHijackHostTextBox
|
||||
// ICMPDelayLabel
|
||||
//
|
||||
this.ICMPHijackHostTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.ICMPHijackCheckBox, "Checked", true));
|
||||
this.ICMPHijackHostTextBox.Enabled = false;
|
||||
this.ICMPHijackHostTextBox.Location = new System.Drawing.Point(253, 78);
|
||||
this.ICMPHijackHostTextBox.Name = "ICMPHijackHostTextBox";
|
||||
this.ICMPHijackHostTextBox.Size = new System.Drawing.Size(191, 23);
|
||||
this.ICMPHijackHostTextBox.TabIndex = 7;
|
||||
this.ICMPHijackHostTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.ICMPDelayLabel.AutoSize = true;
|
||||
this.ICMPDelayLabel.Location = new System.Drawing.Point(30, 110);
|
||||
this.ICMPDelayLabel.Name = "ICMPDelayLabel";
|
||||
this.ICMPDelayLabel.Size = new System.Drawing.Size(100, 17);
|
||||
this.ICMPDelayLabel.TabIndex = 5;
|
||||
this.ICMPDelayLabel.Text = "ICMP Delay(ms)";
|
||||
//
|
||||
// ICMPDelayTextBox
|
||||
//
|
||||
this.ICMPDelayTextBox.Location = new System.Drawing.Point(237, 107);
|
||||
this.ICMPDelayTextBox.Name = "ICMPDelayTextBox";
|
||||
this.ICMPDelayTextBox.ReadOnly = true;
|
||||
this.ICMPDelayTextBox.Size = new System.Drawing.Size(98, 23);
|
||||
this.ICMPDelayTextBox.TabIndex = 6;
|
||||
this.ICMPDelayTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// RedirectorSSCheckBox
|
||||
//
|
||||
this.RedirectorSSCheckBox.AutoSize = true;
|
||||
this.RedirectorSSCheckBox.Location = new System.Drawing.Point(11, 129);
|
||||
this.RedirectorSSCheckBox.Location = new System.Drawing.Point(15, 140);
|
||||
this.RedirectorSSCheckBox.Name = "RedirectorSSCheckBox";
|
||||
this.RedirectorSSCheckBox.Size = new System.Drawing.Size(106, 21);
|
||||
this.RedirectorSSCheckBox.TabIndex = 1;
|
||||
this.RedirectorSSCheckBox.Text = "Redirector SS";
|
||||
this.RedirectorSSCheckBox.Size = new System.Drawing.Size(265, 21);
|
||||
this.RedirectorSSCheckBox.TabIndex = 7;
|
||||
this.RedirectorSSCheckBox.Text = "Redirector built-in Shadowsocks support";
|
||||
this.RedirectorSSCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// ChildProcessHandleCheckBox
|
||||
//
|
||||
this.ChildProcessHandleCheckBox.AutoSize = true;
|
||||
this.ChildProcessHandleCheckBox.Enabled = false;
|
||||
this.ChildProcessHandleCheckBox.Location = new System.Drawing.Point(11, 151);
|
||||
this.ChildProcessHandleCheckBox.Location = new System.Drawing.Point(15, 170);
|
||||
this.ChildProcessHandleCheckBox.Name = "ChildProcessHandleCheckBox";
|
||||
this.ChildProcessHandleCheckBox.Size = new System.Drawing.Size(150, 21);
|
||||
this.ChildProcessHandleCheckBox.TabIndex = 2;
|
||||
this.ChildProcessHandleCheckBox.TabIndex = 8;
|
||||
this.ChildProcessHandleCheckBox.Text = "Child Process Handle";
|
||||
this.ChildProcessHandleCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
@@ -872,12 +870,12 @@ namespace Netch.Forms
|
||||
//
|
||||
// AioDNSTabPage
|
||||
//
|
||||
this.AioDNSTabPage.Controls.Add(this.AioDNSRuleRuleLabel);
|
||||
this.AioDNSTabPage.Controls.Add(this.AioDNSRulePathTextBox);
|
||||
this.AioDNSTabPage.Controls.Add(this.ChinaDNSLabel);
|
||||
this.AioDNSTabPage.Controls.Add(this.ChinaDNSTextBox);
|
||||
this.AioDNSTabPage.Controls.Add(this.OtherDNSLabel);
|
||||
this.AioDNSTabPage.Controls.Add(this.OtherDNSTextBox);
|
||||
this.AioDNSTabPage.Controls.Add(this.AioDNSListenPortLabel);
|
||||
this.AioDNSTabPage.Controls.Add(this.AioDNSListenPortTextBox);
|
||||
this.AioDNSTabPage.Location = new System.Drawing.Point(4, 29);
|
||||
this.AioDNSTabPage.Name = "AioDNSTabPage";
|
||||
this.AioDNSTabPage.Padding = new System.Windows.Forms.Padding(3);
|
||||
@@ -886,57 +884,57 @@ namespace Netch.Forms
|
||||
this.AioDNSTabPage.Text = "AioDNS";
|
||||
this.AioDNSTabPage.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// AioDNSRuleRuleLabel
|
||||
//
|
||||
this.AioDNSRuleRuleLabel.AutoSize = true;
|
||||
this.AioDNSRuleRuleLabel.Location = new System.Drawing.Point(16, 30);
|
||||
this.AioDNSRuleRuleLabel.Name = "AioDNSRuleRuleLabel";
|
||||
this.AioDNSRuleRuleLabel.Size = new System.Drawing.Size(56, 17);
|
||||
this.AioDNSRuleRuleLabel.TabIndex = 0;
|
||||
this.AioDNSRuleRuleLabel.Text = "Rule File";
|
||||
//
|
||||
// AioDNSRulePathTextBox
|
||||
//
|
||||
this.AioDNSRulePathTextBox.Enabled = false;
|
||||
this.AioDNSRulePathTextBox.Location = new System.Drawing.Point(150, 30);
|
||||
this.AioDNSRulePathTextBox.Name = "AioDNSRulePathTextBox";
|
||||
this.AioDNSRulePathTextBox.Size = new System.Drawing.Size(201, 23);
|
||||
this.AioDNSRulePathTextBox.TabIndex = 1;
|
||||
//
|
||||
// ChinaDNSLabel
|
||||
//
|
||||
this.ChinaDNSLabel.AutoSize = true;
|
||||
this.ChinaDNSLabel.Location = new System.Drawing.Point(16, 70);
|
||||
this.ChinaDNSLabel.Location = new System.Drawing.Point(15, 23);
|
||||
this.ChinaDNSLabel.Name = "ChinaDNSLabel";
|
||||
this.ChinaDNSLabel.Size = new System.Drawing.Size(70, 17);
|
||||
this.ChinaDNSLabel.TabIndex = 2;
|
||||
this.ChinaDNSLabel.TabIndex = 0;
|
||||
this.ChinaDNSLabel.Text = "China DNS";
|
||||
//
|
||||
// ChinaDNSTextBox
|
||||
//
|
||||
this.ChinaDNSTextBox.Location = new System.Drawing.Point(150, 70);
|
||||
this.ChinaDNSTextBox.Location = new System.Drawing.Point(150, 20);
|
||||
this.ChinaDNSTextBox.Name = "ChinaDNSTextBox";
|
||||
this.ChinaDNSTextBox.Size = new System.Drawing.Size(201, 23);
|
||||
this.ChinaDNSTextBox.TabIndex = 3;
|
||||
this.ChinaDNSTextBox.TabIndex = 1;
|
||||
this.ChinaDNSTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// OtherDNSLabel
|
||||
//
|
||||
this.OtherDNSLabel.AutoSize = true;
|
||||
this.OtherDNSLabel.Location = new System.Drawing.Point(16, 110);
|
||||
this.OtherDNSLabel.Location = new System.Drawing.Point(15, 63);
|
||||
this.OtherDNSLabel.Name = "OtherDNSLabel";
|
||||
this.OtherDNSLabel.Size = new System.Drawing.Size(71, 17);
|
||||
this.OtherDNSLabel.TabIndex = 4;
|
||||
this.OtherDNSLabel.TabIndex = 2;
|
||||
this.OtherDNSLabel.Text = "Other DNS";
|
||||
//
|
||||
// OtherDNSTextBox
|
||||
//
|
||||
this.OtherDNSTextBox.Location = new System.Drawing.Point(150, 110);
|
||||
this.OtherDNSTextBox.Location = new System.Drawing.Point(150, 60);
|
||||
this.OtherDNSTextBox.Name = "OtherDNSTextBox";
|
||||
this.OtherDNSTextBox.Size = new System.Drawing.Size(201, 23);
|
||||
this.OtherDNSTextBox.TabIndex = 5;
|
||||
this.OtherDNSTextBox.TabIndex = 3;
|
||||
this.OtherDNSTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// AioDNSListenPortLabel
|
||||
//
|
||||
this.AioDNSListenPortLabel.AutoSize = true;
|
||||
this.AioDNSListenPortLabel.Location = new System.Drawing.Point(15, 103);
|
||||
this.AioDNSListenPortLabel.Name = "AioDNSListenPortLabel";
|
||||
this.AioDNSListenPortLabel.Size = new System.Drawing.Size(69, 17);
|
||||
this.AioDNSListenPortLabel.TabIndex = 4;
|
||||
this.AioDNSListenPortLabel.Text = "Listen Port";
|
||||
//
|
||||
// AioDNSListenPortTextBox
|
||||
//
|
||||
this.AioDNSListenPortTextBox.Location = new System.Drawing.Point(150, 100);
|
||||
this.AioDNSListenPortTextBox.Name = "AioDNSListenPortTextBox";
|
||||
this.AioDNSListenPortTextBox.Size = new System.Drawing.Size(80, 23);
|
||||
this.AioDNSListenPortTextBox.TabIndex = 5;
|
||||
this.AioDNSListenPortTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// ControlButton
|
||||
//
|
||||
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
@@ -982,8 +980,6 @@ namespace Netch.Forms
|
||||
this.PortGroupBox.PerformLayout();
|
||||
this.NFTabPage.ResumeLayout(false);
|
||||
this.NFTabPage.PerformLayout();
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.groupBox1.PerformLayout();
|
||||
this.WinTUNTabPage.ResumeLayout(false);
|
||||
this.WinTUNGroupBox.ResumeLayout(false);
|
||||
this.WinTUNGroupBox.PerformLayout();
|
||||
@@ -1067,8 +1063,8 @@ namespace Netch.Forms
|
||||
private System.Windows.Forms.TextBox ttiTextBox;
|
||||
private System.Windows.Forms.CheckBox UseMuxCheckBox;
|
||||
private System.Windows.Forms.TabPage AioDNSTabPage;
|
||||
private System.Windows.Forms.Label AioDNSRuleRuleLabel;
|
||||
private System.Windows.Forms.TextBox AioDNSRulePathTextBox;
|
||||
private System.Windows.Forms.Label AioDNSListenPortLabel;
|
||||
private System.Windows.Forms.TextBox AioDNSListenPortTextBox;
|
||||
private System.Windows.Forms.Label OtherDNSLabel;
|
||||
private System.Windows.Forms.Label ChinaDNSLabel;
|
||||
private System.Windows.Forms.TextBox OtherDNSTextBox;
|
||||
@@ -1078,11 +1074,11 @@ namespace Netch.Forms
|
||||
private System.Windows.Forms.Label ServerPingTypeLabel;
|
||||
private System.Windows.Forms.RadioButton TCPingRadioBtn;
|
||||
private System.Windows.Forms.RadioButton ICMPingRadioBtn;
|
||||
private System.Windows.Forms.ComboBox ProcessProxyProtocolComboBox;
|
||||
private System.Windows.Forms.Label ProcessProxyProtocolLabel;
|
||||
private System.Windows.Forms.CheckBox ICMPHijackCheckBox;
|
||||
private System.Windows.Forms.ComboBox ProcessFilterProtocolComboBox;
|
||||
private System.Windows.Forms.Label ProcessFilterProtocolLabel;
|
||||
private System.Windows.Forms.CheckBox FilterICMPCheckBox;
|
||||
private System.Windows.Forms.CheckBox ChildProcessHandleCheckBox;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.TextBox ICMPHijackHostTextBox;
|
||||
private System.Windows.Forms.TextBox ICMPDelayTextBox;
|
||||
private System.Windows.Forms.Label ICMPDelayLabel;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,3 @@
|
||||
using Netch.Models;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
@@ -8,6 +5,10 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Models;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Forms
|
||||
{
|
||||
@@ -59,11 +60,11 @@ namespace Netch.Forms
|
||||
object[]? stuns;
|
||||
try
|
||||
{
|
||||
stuns = File.ReadLines("bin\\stun.txt").Cast<object>().ToArray();
|
||||
stuns = File.ReadLines(Constants.STUNServersFile).Cast<object>().ToArray();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Warning($"Load stun.txt failed: {e.Message}");
|
||||
Log.Warning(e, "Load stun.txt failed");
|
||||
stuns = null;
|
||||
}
|
||||
|
||||
@@ -98,16 +99,18 @@ namespace Netch.Forms
|
||||
|
||||
#region Process Mode
|
||||
|
||||
BindListComboBox(ProcessFilterProtocolComboBox,
|
||||
s => Global.Settings.Redirector.FilterProtocol = (PortType)Enum.Parse(typeof(PortType), s.ToString(), false),
|
||||
Enum.GetNames(typeof(PortType)),
|
||||
Global.Settings.Redirector.FilterProtocol.ToString());
|
||||
|
||||
BindCheckBox(DNSHijackCheckBox, b => Global.Settings.Redirector.DNSHijack = b, Global.Settings.Redirector.DNSHijack);
|
||||
|
||||
BindTextBox(DNSHijackHostTextBox, s => true, s => Global.Settings.Redirector.DNSHijackHost = s, Global.Settings.Redirector.DNSHijackHost);
|
||||
|
||||
BindCheckBox(ICMPHijackCheckBox, b => Global.Settings.Redirector.ICMPHijack = b, Global.Settings.Redirector.ICMPHijack);
|
||||
BindCheckBox(FilterICMPCheckBox, b => Global.Settings.Redirector.FilterICMP = b, Global.Settings.Redirector.FilterICMP);
|
||||
|
||||
BindTextBox(ICMPHijackHostTextBox,
|
||||
s => IPAddress.TryParse(s, out _),
|
||||
s => Global.Settings.Redirector.ICMPHost = s,
|
||||
Global.Settings.Redirector.ICMPHost);
|
||||
BindTextBox(ICMPDelayTextBox, s => int.TryParse(s, out _), s => { }, Global.Settings.Redirector.ICMPDelay);
|
||||
|
||||
BindCheckBox(RedirectorSSCheckBox, s => Global.Settings.Redirector.RedirectorSS = s, Global.Settings.Redirector.RedirectorSS);
|
||||
|
||||
@@ -115,11 +118,6 @@ namespace Netch.Forms
|
||||
s => Global.Settings.Redirector.ChildProcessHandle = s,
|
||||
Global.Settings.Redirector.ChildProcessHandle);
|
||||
|
||||
BindListComboBox(ProcessProxyProtocolComboBox,
|
||||
s => Global.Settings.Redirector.ProxyProtocol = (PortType)Enum.Parse(typeof(PortType), s.ToString(), false),
|
||||
Enum.GetNames(typeof(PortType)),
|
||||
Global.Settings.Redirector.ProxyProtocol.ToString());
|
||||
|
||||
#endregion
|
||||
|
||||
#region TUN/TAP
|
||||
@@ -211,12 +209,15 @@ namespace Netch.Forms
|
||||
|
||||
#region AioDNS
|
||||
|
||||
BindTextBox(AioDNSRulePathTextBox, _ => true, s => Global.Settings.AioDNS.RulePath = s, Global.Settings.AioDNS.RulePath);
|
||||
|
||||
BindTextBox(ChinaDNSTextBox, _ => true, s => Global.Settings.AioDNS.ChinaDNS = s, Global.Settings.AioDNS.ChinaDNS);
|
||||
|
||||
BindTextBox(OtherDNSTextBox, _ => true, s => Global.Settings.AioDNS.OtherDNS = s, Global.Settings.AioDNS.OtherDNS);
|
||||
|
||||
BindTextBox(AioDNSListenPortTextBox,
|
||||
s => ushort.TryParse(s, out _),
|
||||
s => Global.Settings.AioDNS.ListenPort = ushort.Parse(s),
|
||||
Global.Settings.AioDNS.ListenPort);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -240,7 +241,7 @@ namespace Netch.Forms
|
||||
Show();
|
||||
}
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
private async void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Utils.Utils.ComponentIterator(this, component => Utils.Utils.ChangeControlForeColor(component, Color.Black));
|
||||
|
||||
@@ -264,7 +265,7 @@ namespace Netch.Forms
|
||||
|
||||
Utils.Utils.RegisterNetchStartupItem();
|
||||
|
||||
Configuration.Save();
|
||||
await Configuration.SaveAsync();
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -57,9 +57,9 @@ namespace Netch.Forms
|
||||
Global.Settings.SubscribeLink[index].Enable = SubscribeLinkListView.Items[index].Checked;
|
||||
}
|
||||
|
||||
private void SubscribeForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
private async void SubscribeForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Configuration.Save();
|
||||
await Configuration.SaveAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using Netch.Forms;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Models.Loggers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch
|
||||
{
|
||||
@@ -34,21 +32,14 @@ namespace Netch
|
||||
{
|
||||
NetchExecutable = Application.ExecutablePath;
|
||||
NetchDir = Application.StartupPath;
|
||||
#if DEBUG
|
||||
Logger = new ConsoleLogger();
|
||||
#else
|
||||
Logger = new FileLogger();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static ILogger Logger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 主窗体的静态实例
|
||||
/// </summary>
|
||||
public static MainForm MainForm => LazyMainForm.Value;
|
||||
|
||||
public static JsonSerializerOptions NewDefaultJsonSerializerOptions => new()
|
||||
public static JsonSerializerOptions NewCustomJsonSerializerOptions() => new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
IgnoreNullValues = true,
|
||||
|
||||
@@ -2,14 +2,8 @@
|
||||
{
|
||||
public interface IController
|
||||
{
|
||||
/// <summary>
|
||||
/// 控制器名
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public void Stop();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace Netch.Interfaces
|
||||
{
|
||||
public interface ILogger
|
||||
{
|
||||
void Info(string text);
|
||||
void Warning(string text);
|
||||
void Error(string text);
|
||||
void Debug(string s);
|
||||
void ShowLog();
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,6 @@ namespace Netch.Interfaces
|
||||
{
|
||||
public interface IModeController : IController
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否成功</returns>
|
||||
public abstract void Start(in Mode mode);
|
||||
public void Start(Server server, Mode mode);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Netch.Models;
|
||||
using Netch.Servers;
|
||||
|
||||
namespace Netch.Interfaces
|
||||
{
|
||||
@@ -8,13 +9,7 @@ namespace Netch.Interfaces
|
||||
|
||||
public string? LocalAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="s">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public abstract void Start(in Server s, in Mode mode);
|
||||
public Socks5 Start(in Server s);
|
||||
}
|
||||
|
||||
public static class ServerControllerExtension
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Interops
|
||||
{
|
||||
@@ -9,6 +10,7 @@ namespace Netch.Interops
|
||||
|
||||
public static bool Dial(NameList name, string value)
|
||||
{
|
||||
Log.Debug($"[aiodns] Dial {name}: {value}");
|
||||
return aiodns_dial(name, Encoding.UTF8.GetBytes(value));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Interops
|
||||
{
|
||||
@@ -42,7 +43,7 @@ namespace Netch.Interops
|
||||
|
||||
public static bool Dial(NameList name, string value)
|
||||
{
|
||||
Global.Logger.Debug($"Dial {name} {value}");
|
||||
Log.Debug($"[Redirector] Dial {name}: {value}");
|
||||
return aio_dial(name, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Interops
|
||||
{
|
||||
@@ -35,13 +36,13 @@ namespace Netch.Interops
|
||||
|
||||
public static bool Dial(NameList name, string value)
|
||||
{
|
||||
Global.Logger.Debug($"Dial {name} {value}");
|
||||
Log.Debug( $"[tun2socks] Dial {name}: {value}");
|
||||
return tun_dial(name, Encoding.UTF8.GetBytes(value));
|
||||
}
|
||||
|
||||
public static bool Init()
|
||||
{
|
||||
Global.Logger.Debug("tun2socks init");
|
||||
Log.Debug("[tun2socks] init");
|
||||
return tun_init();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace Netch.Models
|
||||
{
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR,
|
||||
DEBUG
|
||||
ERROR
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using Netch.Interfaces;
|
||||
using System;
|
||||
|
||||
namespace Netch.Models.Loggers
|
||||
{
|
||||
public class ConsoleLogger : ILogger
|
||||
{
|
||||
public void Info(string text)
|
||||
{
|
||||
Write(text, LogLevel.INFO);
|
||||
}
|
||||
|
||||
public void Warning(string text)
|
||||
{
|
||||
Write(text, LogLevel.WARNING);
|
||||
}
|
||||
|
||||
public void Error(string text)
|
||||
{
|
||||
Write(text, LogLevel.ERROR);
|
||||
}
|
||||
|
||||
private void Write(string text, LogLevel logLevel)
|
||||
{
|
||||
var contents = $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Constants.EOF}";
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.DEBUG:
|
||||
case LogLevel.INFO:
|
||||
case LogLevel.WARNING:
|
||||
Console.Write(contents);
|
||||
break;
|
||||
case LogLevel.ERROR:
|
||||
Console.Error.Write(contents);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void Debug(string s)
|
||||
{
|
||||
#if DEBUG
|
||||
Write(s, LogLevel.DEBUG);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void ShowLog()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using Netch.Interfaces;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Netch.Models.Loggers
|
||||
{
|
||||
public class FileLogger : ILogger
|
||||
{
|
||||
public string LogFile { get; set; } = Path.Combine(Global.NetchDir, "logging\\application.log");
|
||||
|
||||
private readonly object _fileLock = new();
|
||||
|
||||
public void Info(string text)
|
||||
{
|
||||
Write(text, LogLevel.INFO);
|
||||
}
|
||||
|
||||
public void Warning(string text)
|
||||
{
|
||||
Write(text, LogLevel.WARNING);
|
||||
}
|
||||
|
||||
public void Error(string text)
|
||||
{
|
||||
Write(text, LogLevel.ERROR);
|
||||
}
|
||||
|
||||
public void Write(string text, LogLevel logLevel)
|
||||
{
|
||||
var contents = $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Constants.EOF}";
|
||||
|
||||
lock (_fileLock)
|
||||
File.AppendAllText(LogFile, contents);
|
||||
}
|
||||
|
||||
public void Debug(string s)
|
||||
{
|
||||
#if DEBUG
|
||||
Write(s, LogLevel.DEBUG);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void ShowLog()
|
||||
{
|
||||
Utils.Utils.Open(LogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,47 @@
|
||||
using Netch.Utils;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Netch.Enums;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Models
|
||||
{
|
||||
public class Mode
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="fullName">Mode File FullPath</param>
|
||||
/// <exception cref="FormatException"></exception>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
private List<string>? _content;
|
||||
|
||||
public Mode(string? fullName)
|
||||
{
|
||||
FullName = fullName;
|
||||
if (FullName == null || !File.Exists(FullName))
|
||||
return;
|
||||
|
||||
(Remark, Type) = ReadHead(FullName);
|
||||
Load();
|
||||
}
|
||||
|
||||
public string? FullName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 规则
|
||||
/// </summary>
|
||||
public List<string> Content => _content ??= ReadContent();
|
||||
|
||||
private List<string>? _content;
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string Remark { get; set; } = "";
|
||||
|
||||
public ModeType Type { get; set; } = ModeType.Process;
|
||||
|
||||
/// <summary>
|
||||
/// 文件相对路径(必须是存在的文件)
|
||||
/// </summary>
|
||||
public string? RelativePath => FullName == null ? null : ModeHelper.GetRelativePath(FullName);
|
||||
|
||||
private void Load()
|
||||
{
|
||||
if (FullName == null)
|
||||
return;
|
||||
|
||||
(Remark, Type) = ReadHead(FullName);
|
||||
_content = null;
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetRules()
|
||||
{
|
||||
var result = new List<string>();
|
||||
foreach (var s in Content)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s))
|
||||
@@ -76,15 +69,14 @@ namespace Netch.Models
|
||||
if (mode.Content.Any(rule => rule.StartsWith(include)))
|
||||
throw new Exception("Cannot reference mode that reference other mode");
|
||||
|
||||
result.AddRange(mode.GetRules());
|
||||
foreach (var rule in mode.GetRules())
|
||||
yield return rule;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(s);
|
||||
yield return s;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static (string, ModeType) ReadHead(string fileName)
|
||||
@@ -93,13 +85,15 @@ namespace Netch.Models
|
||||
if (text.First() != '#')
|
||||
throw new FormatException($"{fileName} head not found at Line 0");
|
||||
|
||||
var split = text[1..].SplitTrimEntries(',');
|
||||
var strings = text[1..].SplitTrimEntries(',');
|
||||
|
||||
var remark = strings[0];
|
||||
var typeNumber = int.TryParse(strings.ElementAtOrDefault(1), out var type) ? type : 0;
|
||||
|
||||
var typeNumber = int.TryParse(split.ElementAtOrDefault(1), out var type) ? type : 0;
|
||||
if (!Enum.GetValues(typeof(ModeType)).Cast<int>().Contains(typeNumber))
|
||||
throw new NotSupportedException($"Not support mode \"{typeNumber}\".");
|
||||
|
||||
return (split[0], (ModeType)typeNumber);
|
||||
return (remark, (ModeType)typeNumber);
|
||||
}
|
||||
|
||||
private List<string> ReadContent()
|
||||
@@ -110,11 +104,6 @@ namespace Netch.Models
|
||||
return File.ReadLines(FullName).Skip(1).ToList();
|
||||
}
|
||||
|
||||
public void ResetContent()
|
||||
{
|
||||
_content = null;
|
||||
}
|
||||
|
||||
public void WriteFile()
|
||||
{
|
||||
var dir = Path.GetDirectoryName(FullName)!;
|
||||
@@ -126,10 +115,6 @@ namespace Netch.Models
|
||||
File.WriteAllText(FullName!, content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取备注
|
||||
/// </summary>
|
||||
/// <returns>备注</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[{(int)Type + 1}] {i18N.Translate(Remark)}";
|
||||
@@ -141,7 +126,8 @@ namespace Netch.Models
|
||||
/// 是否会转发 UDP
|
||||
public static bool TestNatRequired(this Mode mode)
|
||||
{
|
||||
return mode.Type is ModeType.Process or ModeType.BypassRuleIPs;
|
||||
return mode.Type is ModeType.Process && Global.Settings.Redirector.FilterProtocol.HasFlag(PortType.UDP) ||
|
||||
mode.Type is ModeType.BypassRuleIPs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,13 @@ namespace Netch.Models
|
||||
};
|
||||
}
|
||||
|
||||
public static (NetRoute, IPAddress address) GetBestRouteTemplate()
|
||||
public static NetRoute GetBestRouteTemplate()
|
||||
{
|
||||
if (IpHlpApi.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse("114.114.114.114").GetAddressBytes(), 0), 0, out var route) != 0)
|
||||
throw new MessageException("GetBestRoute 搜索失败");
|
||||
|
||||
var address = new IPAddress(route.dwForwardNextHop.S_addr);
|
||||
var gateway = new IPAddress(route.dwForwardNextHop.S_un_b);
|
||||
return (TemplateBuilder(gateway.ToString(), (int)route.dwForwardIfIndex), address);
|
||||
return TemplateBuilder(gateway.ToString(), (int)route.dwForwardIfIndex);
|
||||
}
|
||||
|
||||
public int InterfaceIndex;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using Netch.Utils;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Models
|
||||
{
|
||||
public class Server : ICloneable
|
||||
public abstract class Server : ICloneable
|
||||
{
|
||||
/// <summary>
|
||||
/// 延迟
|
||||
@@ -48,6 +48,7 @@ namespace Netch.Models
|
||||
// ReSharper disable once CollectionNeverUpdated.Global
|
||||
public Dictionary<string, object> ExtensionData { get; set; } = new();
|
||||
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return MemberwiseClone();
|
||||
@@ -77,6 +78,7 @@ namespace Netch.Models
|
||||
return $"[{shortName}][{Group}] {remark}";
|
||||
}
|
||||
|
||||
public abstract string MaskedData();
|
||||
/// <summary>
|
||||
/// 测试延迟
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Netch.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Netch.Models
|
||||
{
|
||||
@@ -80,9 +81,7 @@ namespace Netch.Models
|
||||
|
||||
public string OtherDNS { get; set; } = "tcp://1.1.1.1:53";
|
||||
|
||||
public ushort ListenPort { get; set; } = 53;
|
||||
|
||||
public string RulePath { get; set; } = "bin\\aiodns.conf";
|
||||
public ushort ListenPort { get; set; } = 253;
|
||||
}
|
||||
|
||||
public class RedirectorConfig
|
||||
@@ -90,7 +89,7 @@ namespace Netch.Models
|
||||
/// <summary>
|
||||
/// 不代理TCP
|
||||
/// </summary>
|
||||
public PortType ProxyProtocol { get; set; } = PortType.Both;
|
||||
public PortType FilterProtocol { get; set; } = PortType.Both;
|
||||
|
||||
/// <summary>
|
||||
/// 是否开启DNS转发
|
||||
@@ -102,9 +101,10 @@ namespace Netch.Models
|
||||
/// </summary>
|
||||
public string DNSHijackHost { get; set; } = "1.1.1.1:53";
|
||||
|
||||
public string ICMPHost { get; set; } = "1.2.4.8";
|
||||
[JsonIgnore]
|
||||
public int ICMPDelay { get; } = 0;
|
||||
|
||||
public bool ICMPHijack { get; set; } = false;
|
||||
public bool FilterICMP { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用RDR内置SS
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms;
|
||||
using Netch.Utils;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using static Vanara.PInvoke.Kernel32;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms;
|
||||
using Netch.Services;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace Netch
|
||||
{
|
||||
public static class Netch
|
||||
{
|
||||
public static readonly SingleInstance.SingleInstance SingleInstance = new($"Global\\{nameof(Netch)}");
|
||||
public static readonly SingleInstance.SingleInstanceService SingleInstance = new($"Global\\{nameof(Netch)}");
|
||||
|
||||
public static HWND ConsoleHwnd { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用程序的主入口点
|
||||
@@ -22,13 +27,6 @@ namespace Netch
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
#if DEBUG
|
||||
AttachAllocConsole();
|
||||
#else
|
||||
if (args.Contains(Constants.Parameter.Console))
|
||||
AttachAllocConsole();
|
||||
#endif
|
||||
|
||||
if (args.Contains(Constants.Parameter.ForceUpdate))
|
||||
Flags.AlwaysShowNewVersionFound = true;
|
||||
|
||||
@@ -36,18 +34,17 @@ namespace Netch
|
||||
Directory.SetCurrentDirectory(Global.NetchDir);
|
||||
var binPath = Path.Combine(Global.NetchDir, "bin");
|
||||
Environment.SetEnvironmentVariable("PATH", $"{Environment.GetEnvironmentVariable("PATH")};{binPath}");
|
||||
AddDllDirectory(binPath);
|
||||
|
||||
Updater.Updater.CleanOld(Global.NetchDir);
|
||||
Updater.CleanOld(Global.NetchDir);
|
||||
|
||||
// 预创建目录
|
||||
var directories = new[] { "mode\\Custom", "data", "i18n", "logging" };
|
||||
var directories = new[] {"mode\\Custom", "data", "i18n", "logging"};
|
||||
foreach (var item in directories)
|
||||
if (!Directory.Exists(item))
|
||||
Directory.CreateDirectory(item);
|
||||
|
||||
// 加载配置
|
||||
Configuration.Load();
|
||||
Configuration.LoadAsync().Wait();
|
||||
|
||||
if (!SingleInstance.IsFirstInstance)
|
||||
{
|
||||
@@ -57,7 +54,6 @@ namespace Netch
|
||||
}
|
||||
|
||||
SingleInstance.ArgumentsReceived.Subscribe(SingleInstance_ArgumentsReceived);
|
||||
SingleInstance.ListenForArgumentsFromSuccessiveInstances();
|
||||
|
||||
// 清理上一次的日志文件,防止淤积占用磁盘空间
|
||||
if (Directory.Exists("logging"))
|
||||
@@ -71,6 +67,10 @@ namespace Netch
|
||||
dir.Delete(true);
|
||||
}
|
||||
|
||||
InitConsole();
|
||||
|
||||
CreateLogger();
|
||||
|
||||
// 加载语言
|
||||
i18N.Load(Global.Settings.Language);
|
||||
|
||||
@@ -80,29 +80,62 @@ namespace Netch
|
||||
Environment.Exit(2);
|
||||
}
|
||||
|
||||
Global.Logger.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
|
||||
Task.Run(() => { Global.Logger.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}"); });
|
||||
Task.Run(LogEnvironment);
|
||||
|
||||
// 绑定错误捕获
|
||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
Application.ThreadException += Application_OnException;
|
||||
Application.ApplicationExit += Application_OnExit;
|
||||
|
||||
Application.SetHighDpiMode(HighDpiMode.SystemAware);
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(Global.MainForm);
|
||||
}
|
||||
|
||||
private static void AttachAllocConsole()
|
||||
private static void LogEnvironment()
|
||||
{
|
||||
if (!AttachConsole(ATTACH_PARENT_PROCESS))
|
||||
AllocConsole();
|
||||
Log.Information("Netch Version: {Version}", $"{UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
|
||||
Log.Information("Environment: {OSVersion}", Environment.OSVersion);
|
||||
Log.Information("SHA256: {Hash}", $"{Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}");
|
||||
if (Log.IsEnabled(LogEventLevel.Debug))
|
||||
Log.Debug("Third-party Drivers:\n{Drivers}", string.Join("\n", SystemInfo.SystemDrivers(false)));
|
||||
}
|
||||
|
||||
public static void Application_OnException(object sender, ThreadExceptionEventArgs e)
|
||||
private static void InitConsole()
|
||||
{
|
||||
Global.Logger.Error(e.Exception.ToString());
|
||||
Global.Logger.ShowLog();
|
||||
Kernel32.AllocConsole();
|
||||
|
||||
ConsoleHwnd = Kernel32.GetConsoleWindow();
|
||||
#if RELEASE
|
||||
User32.ShowWindow(ConsoleHwnd, ShowWindowCommand.SW_HIDE);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void CreateLogger()
|
||||
{
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
#if DEBUG
|
||||
.MinimumLevel.Verbose()
|
||||
.WriteTo.Async(c => c.Debug(outputTemplate: Constants.OutputTemplate))
|
||||
#else
|
||||
.MinimumLevel.Debug()
|
||||
#endif
|
||||
.WriteTo.Async(c => c.File(Path.Combine(Global.NetchDir, Constants.LogFile),
|
||||
outputTemplate: Constants.OutputTemplate,
|
||||
rollOnFileSizeLimit: false))
|
||||
.MinimumLevel.Override(@"Microsoft", LogEventLevel.Information)
|
||||
.Enrich.FromLogContext()
|
||||
.CreateLogger();
|
||||
}
|
||||
|
||||
private static void Application_OnException(object sender, ThreadExceptionEventArgs e)
|
||||
{
|
||||
Log.Error(e.Exception, "未处理异常");
|
||||
}
|
||||
|
||||
private static void Application_OnExit(object? sender, EventArgs eventArgs)
|
||||
{
|
||||
Log.CloseAndFlush();
|
||||
}
|
||||
|
||||
private static void SingleInstance_ArgumentsReceived(IEnumerable<string> args)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
@@ -36,19 +36,25 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HMBSbige.SingleInstance" Version="5.0.0" />
|
||||
<PackageReference Include="HMBSbige.SingleInstance" Version="5.0.7" />
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.68" GeneratePathProperty="true" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.70" GeneratePathProperty="true" />
|
||||
<PackageReference Include="Nullable.Extended.Analyzer" Version="1.2.4089">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="4.1.2" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Debug" Version="2.0.0" Condition="'$(Configuration)'=='Debug'" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
|
||||
<PackageReference Include="System.Management" Version="5.0.0" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
<PackageReference Include="TaskScheduler" Version="2.9.1" />
|
||||
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.9" />
|
||||
<PackageReference Include="Vanara.PInvoke.IpHlpApi" Version="3.3.10" />
|
||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
|
||||
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.9" />
|
||||
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.10" />
|
||||
<PackageReference Include="WindowsFirewallHelper" Version="2.0.4.70-beta2" />
|
||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="5.0.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
|
||||
@@ -65,7 +71,7 @@
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Forms\Mode\Route.cs" />
|
||||
<Compile Update="Forms\Mode\RouteForm.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -84,6 +90,7 @@
|
||||
|
||||
<Target Condition="'$(PublishSingleFile)' == 'true'" AfterTargets="_ComputeFilesToBundle" Name="RemoveDupeAssemblies">
|
||||
<ItemGroup>
|
||||
<_FilesToBundle Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\build\native\x86\**" />
|
||||
<_FilesToBundle Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\lib\netstandard1.6\Dia2Lib.dll" />
|
||||
<_FilesToBundle Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\lib\netstandard1.6\OSExtensions.dll" />
|
||||
<_FilesToBundle Remove="$(PkgMicrosoft_Diagnostics_Tracing_TraceEvent)\lib\netstandard1.6\TraceReloggerLib.dll" />
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
"Stopping": "正在停止中",
|
||||
"Stopped": "已停止",
|
||||
"Starting {0}": "正在启动 {0}",
|
||||
"Starting NatTester": "正在启动 NAT 测试",
|
||||
"SetupBypass": "设置绕行规则",
|
||||
"Testing NAT": "正在测试 NAT",
|
||||
"Setup Route Table Rule": "配置路由规则",
|
||||
"Test failed": "测试失败",
|
||||
"Starting update subscription": "正在更新订阅",
|
||||
"Subscription updated": "订阅更新完毕",
|
||||
@@ -48,9 +48,11 @@
|
||||
"Transfer Protocol": "传输协议",
|
||||
"Fake Type": "伪装类型",
|
||||
"Host": "主机",
|
||||
"Path": "路径",
|
||||
"Path": "路径/服务名称",
|
||||
"QUIC Security": "QUIC 加密方式",
|
||||
"QUIC Secret": "QUIC 加密密钥",
|
||||
"GRPC Mode": "QUIC 模式",
|
||||
"GRPC ServiceName": "QUIC 服务名称",
|
||||
"TLS Secure": "TLS 底层传输安全",
|
||||
"Use Mux": "Mux 多路复用",
|
||||
"Encrypt Method": "加密方式",
|
||||
@@ -87,6 +89,7 @@
|
||||
"Remove Netch Firewall Rules": "移除 Netch 防火墙规则",
|
||||
|
||||
"Open Directory": "打开目录",
|
||||
"Show/Hide Console": "显示/隐藏控制台",
|
||||
|
||||
"About": "关于",
|
||||
"FAQ": "常见问题",
|
||||
@@ -147,13 +150,14 @@
|
||||
"Check update when opened": "打开软件时检查更新",
|
||||
"Check Beta update": "检查 Beta 更新",
|
||||
"Update Servers when opened": "打开软件时更新服务器",
|
||||
"Proxy Protocol": "代理协议",
|
||||
"Filter Protocol": "Filter 协议",
|
||||
"Handle process's DNS Hijack": "被代理进程 DNS 劫持",
|
||||
"Global ICMP Hijack": "全局 ICMP 劫持",
|
||||
"Child Process Handle": "子进程代理",
|
||||
"ProfileCount": "快捷配置数量",
|
||||
"Delay test after start": "启动后延迟测试",
|
||||
"ServerPingType": "测速方式",
|
||||
"ICMP Delay(ms)": "ICMP 延迟(毫秒)",
|
||||
"Redirector built-in Shadowsocks support": "Redirector 内建 Shadowsocks 支持",
|
||||
"Profile Count": "快捷配置数量",
|
||||
"Delay test after start(sec)": "启动后延迟测试(秒)",
|
||||
"Ping Protocol": "延迟测试协议",
|
||||
"Detection Tick(sec)": "检测心跳(秒)",
|
||||
"STUN Server": "STUN 服务器",
|
||||
"Language": "语言",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Netch.Controllers;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
@@ -7,19 +8,21 @@ namespace Netch.Servers.Shadowsocks
|
||||
{
|
||||
public class SSController : Guard, IServerController
|
||||
{
|
||||
public override string MainFile { get; protected set; } = "Shadowsocks.exe";
|
||||
public SSController() : base("Shadowsocks.exe")
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] { "listening at" };
|
||||
protected override IEnumerable<string> StartedKeywords => new[] { "listening at" };
|
||||
|
||||
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] { "Invalid config path", "usage", "plugin service exit unexpectedly" };
|
||||
protected override IEnumerable<string> FailedKeywords => new[] { "Invalid config path", "usage", "plugin service exit unexpectedly" };
|
||||
|
||||
public override string Name { get; } = "Shadowsocks";
|
||||
public override string Name => "Shadowsocks";
|
||||
|
||||
public ushort? Socks5LocalPort { get; set; }
|
||||
|
||||
public string? LocalAddress { get; set; }
|
||||
|
||||
public void Start(in Server s, in Mode mode)
|
||||
public Socks5 Start(in Server s)
|
||||
{
|
||||
var server = (Shadowsocks)s;
|
||||
|
||||
@@ -36,7 +39,8 @@ namespace Netch.Servers.Shadowsocks
|
||||
plugin_opts = server.PluginOption
|
||||
};
|
||||
|
||||
StartInstanceAuto(command.ToString());
|
||||
StartGuard(command.ToString());
|
||||
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname);
|
||||
}
|
||||
|
||||
[Verb]
|
||||
@@ -56,24 +60,14 @@ namespace Netch.Servers.Shadowsocks
|
||||
|
||||
public bool u { get; set; }
|
||||
|
||||
[Full]
|
||||
[Optional]
|
||||
public string? plugin { get; set; }
|
||||
[Full] [Optional] public string? plugin { get; set; }
|
||||
|
||||
[Full]
|
||||
[Optional]
|
||||
[RealName("plugin-opts")]
|
||||
public string? plugin_opts { get; set; }
|
||||
|
||||
[Full]
|
||||
[Quote]
|
||||
[Optional]
|
||||
public string? acl { get; set; }
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
[Full] [Quote] [Optional] public string? acl { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using Netch.Models;
|
||||
using Netch.Servers.Shadowsocks.Form;
|
||||
using Netch.Servers.Shadowsocks.Models.SSD;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Servers.Shadowsocks
|
||||
{
|
||||
@@ -65,10 +66,8 @@ namespace Netch.Servers.Shadowsocks
|
||||
var server = (Shadowsocks)s;
|
||||
if (!SSGlobal.EncryptMethods.Contains(server.EncryptMethod))
|
||||
{
|
||||
Global.Logger.Error($"不支持的 SS 加密方式:{server.EncryptMethod}");
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Log.Warning("不支持的 SS 加密方式:{Method}", server.EncryptMethod);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -79,17 +78,17 @@ namespace Netch.Servers.Shadowsocks
|
||||
var json = JsonSerializer.Deserialize<Main>(ShareLink.URLSafeBase64Decode(s.Substring(6)))!;
|
||||
|
||||
return json.servers.Select(server => new Shadowsocks
|
||||
{
|
||||
Remark = server.remarks,
|
||||
Hostname = server.server,
|
||||
Port = server.port != 0 ? server.port : json.port,
|
||||
Password = server.password ?? json.password,
|
||||
EncryptMethod = server.encryption ?? json.encryption,
|
||||
Plugin = string.IsNullOrEmpty(json.plugin) ? string.IsNullOrEmpty(server.plugin) ? null : server.plugin : json.plugin,
|
||||
PluginOption = string.IsNullOrEmpty(json.plugin_options)
|
||||
{
|
||||
Remark = server.remarks,
|
||||
Hostname = server.server,
|
||||
Port = server.port != 0 ? server.port : json.port,
|
||||
Password = server.password ?? json.password,
|
||||
EncryptMethod = server.encryption ?? json.encryption,
|
||||
Plugin = string.IsNullOrEmpty(json.plugin) ? string.IsNullOrEmpty(server.plugin) ? null : server.plugin : json.plugin,
|
||||
PluginOption = string.IsNullOrEmpty(json.plugin_options)
|
||||
? string.IsNullOrEmpty(server.plugin_options) ? null : server.plugin_options
|
||||
: json.plugin_options
|
||||
})
|
||||
})
|
||||
.Where(CheckServer);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
using Netch.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Servers.Shadowsocks
|
||||
{
|
||||
public class Shadowsocks : Server
|
||||
{
|
||||
public override string Type { get; } = "SS";
|
||||
public override string MaskedData()
|
||||
{
|
||||
return $"{EncryptMethod} + {Plugin}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加密方式
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Netch.Controllers;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
@@ -7,19 +8,21 @@ namespace Netch.Servers.ShadowsocksR
|
||||
{
|
||||
public class SSRController : Guard, IServerController
|
||||
{
|
||||
public override string MainFile { get; protected set; } = "ShadowsocksR.exe";
|
||||
public SSRController() : base("ShadowsocksR.exe")
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] { "listening at" };
|
||||
protected override IEnumerable<string> StartedKeywords => new[] { "listening at" };
|
||||
|
||||
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] { "Invalid config path", "usage" };
|
||||
protected override IEnumerable<string> FailedKeywords => new[] { "Invalid config path", "usage" };
|
||||
|
||||
public override string Name { get; } = "ShadowsocksR";
|
||||
public override string Name => "ShadowsocksR";
|
||||
|
||||
public ushort? Socks5LocalPort { get; set; }
|
||||
|
||||
public string? LocalAddress { get; set; }
|
||||
|
||||
public void Start(in Server s, in Mode mode)
|
||||
public Socks5 Start(in Server s)
|
||||
{
|
||||
var server = (ShadowsocksR)s;
|
||||
|
||||
@@ -39,7 +42,8 @@ namespace Netch.Servers.ShadowsocksR
|
||||
u = true
|
||||
};
|
||||
|
||||
StartInstanceAuto(command.ToString());
|
||||
StartGuard(command.ToString());
|
||||
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(),server.Hostname);
|
||||
}
|
||||
|
||||
[Verb]
|
||||
@@ -79,10 +83,5 @@ namespace Netch.Servers.ShadowsocksR
|
||||
[Optional]
|
||||
public string? acl { get; set; }
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Netch.Models;
|
||||
using Netch.Servers.Shadowsocks;
|
||||
using Netch.Servers.ShadowsocksR.Form;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Servers.ShadowsocksR
|
||||
{
|
||||
@@ -146,19 +147,19 @@ namespace Netch.Servers.ShadowsocksR
|
||||
var server = (ShadowsocksR)s;
|
||||
if (!SSRGlobal.EncryptMethods.Contains(server.EncryptMethod))
|
||||
{
|
||||
Global.Logger.Error($"不支持的 SSR 加密方式:{server.EncryptMethod}");
|
||||
Log.Error("不支持的 SSR 加密方式:{Method}", server.EncryptMethod);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SSRGlobal.Protocols.Contains(server.Protocol))
|
||||
{
|
||||
Global.Logger.Error($"不支持的 SSR 协议:{server.Protocol}");
|
||||
Log.Error("不支持的 SSR 协议:{Protocol}", server.Protocol);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SSRGlobal.OBFSs.Contains(server.OBFS))
|
||||
{
|
||||
Global.Logger.Error($"不支持的 SSR 混淆:{server.OBFS}");
|
||||
Log.Error("不支持的 SSR 混淆:{Obfs}", server.OBFS);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
using Netch.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Servers.ShadowsocksR
|
||||
{
|
||||
public class ShadowsocksR : Server
|
||||
{
|
||||
public override string Type { get; } = "SSR";
|
||||
public override string MaskedData()
|
||||
{
|
||||
return $"{EncryptMethod} + {Protocol} + {OBFS}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 密码
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Netch.Forms;
|
||||
|
||||
namespace Netch.Servers.Socks5.Form
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class Socks5Form : ServerForm
|
||||
{
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
using Netch.Models;
|
||||
using Netch.Servers.V2ray;
|
||||
using Netch.Servers;
|
||||
|
||||
namespace Netch.Servers.Socks5
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class S5Controller : V2rayController
|
||||
{
|
||||
public override string Name { get; } = "Socks5";
|
||||
|
||||
public override void Start(in Server s, in Mode mode)
|
||||
public override Socks5 Start(in Server s)
|
||||
{
|
||||
var server = (Socks5)s;
|
||||
if (server.Auth())
|
||||
base.Start(s, mode);
|
||||
base.Start(s);
|
||||
|
||||
return server;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Socks5.Form;
|
||||
|
||||
namespace Netch.Servers.Socks5
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class S5Util : IServerUtil
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Servers.Socks5
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class Socks5 : Server
|
||||
{
|
||||
@@ -16,6 +16,27 @@ namespace Netch.Servers.Socks5
|
||||
|
||||
public override string Type { get; } = "Socks5";
|
||||
|
||||
public override string MaskedData()
|
||||
{
|
||||
return $"Auth: {Auth()}";
|
||||
}
|
||||
|
||||
public Socks5()
|
||||
{
|
||||
}
|
||||
|
||||
public Socks5(string hostname, ushort port)
|
||||
{
|
||||
Hostname = hostname;
|
||||
Port = port;
|
||||
}
|
||||
|
||||
public Socks5(string hostname, ushort port, string username, string password) : this(hostname, port)
|
||||
{
|
||||
Username = username;
|
||||
Password = password;
|
||||
}
|
||||
|
||||
public bool Auth()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password);
|
||||
|
||||
17
Netch/Servers/Socks5/Socks5Bridge.cs
Normal file
17
Netch/Servers/Socks5/Socks5Bridge.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Netch.Servers
|
||||
{
|
||||
// TODO rename it
|
||||
/// <summary>
|
||||
/// Encrypted proxy client's local socks5 server
|
||||
/// (<see cref="RemoteHostname"/> property is used for saving remote address/hostname for special use)
|
||||
/// </summary>
|
||||
public class Socks5Bridge : Socks5
|
||||
{
|
||||
public Socks5Bridge(string hostname, ushort port, string remoteHostname) : base(hostname, port)
|
||||
{
|
||||
RemoteHostname = remoteHostname;
|
||||
}
|
||||
|
||||
public string RemoteHostname { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Netch.Forms;
|
||||
|
||||
namespace Netch.Servers.Trojan.Form
|
||||
namespace Netch.Servers.Form
|
||||
{
|
||||
public class TrojanForm : ServerForm
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#nullable disable
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Netch.Servers.Trojan.Models
|
||||
namespace Netch.Servers.Models
|
||||
{
|
||||
public class TrojanConfig
|
||||
{
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Servers.Trojan
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class Trojan : Server
|
||||
{
|
||||
public override string Type { get; } = "Trojan";
|
||||
public override string MaskedData()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 密码
|
||||
|
||||
@@ -1,53 +1,57 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using Netch.Controllers;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Trojan.Models;
|
||||
using Netch.Servers.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Servers.Trojan
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class TrojanController : Guard, IServerController
|
||||
{
|
||||
public override string MainFile { get; protected set; } = "Trojan.exe";
|
||||
public TrojanController() : base("Trojan.exe")
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] { "started" };
|
||||
protected override IEnumerable<string> StartedKeywords => new[] { "started" };
|
||||
|
||||
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] { "exiting" };
|
||||
protected override IEnumerable<string> FailedKeywords => new[] { "exiting" };
|
||||
|
||||
public override string Name { get; } = "Trojan";
|
||||
public override string Name => "Trojan";
|
||||
|
||||
public ushort? Socks5LocalPort { get; set; }
|
||||
|
||||
public string? LocalAddress { get; set; }
|
||||
|
||||
public void Start(in Server s, in Mode mode)
|
||||
public Socks5 Start(in Server s)
|
||||
{
|
||||
var server = (Trojan)s;
|
||||
var trojanConfig = new TrojanConfig
|
||||
{
|
||||
local_addr = this.LocalAddress(),
|
||||
local_port = this.Socks5LocalPort(),
|
||||
remote_addr = server.Hostname,
|
||||
remote_addr = server.AutoResolveHostname(),
|
||||
remote_port = server.Port,
|
||||
password = new List<string>
|
||||
{
|
||||
server.Password
|
||||
},
|
||||
ssl = new TrojanSSL
|
||||
{
|
||||
sni = server.Host.ValueOrDefault() ?? (Global.Settings.ResolveServerHostname ? server.Hostname : "")
|
||||
}
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(server.Host))
|
||||
trojanConfig.ssl.sni = server.Host;
|
||||
using (var fileStream = new FileStream(Constants.TempConfig, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
JsonSerializer.SerializeAsync(fileStream, trojanConfig, Global.NewCustomJsonSerializerOptions()).Wait();
|
||||
}
|
||||
|
||||
File.WriteAllBytes("data\\last.json", JsonSerializer.SerializeToUtf8Bytes(trojanConfig, Global.NewDefaultJsonSerializerOptions));
|
||||
|
||||
StartInstanceAuto("-c ..\\data\\last.json");
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
StartGuard("-c ..\\data\\last.json");
|
||||
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Trojan.Form;
|
||||
using Netch.Servers.Form;
|
||||
|
||||
namespace Netch.Servers.Trojan
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class TrojanUtil : IServerUtil
|
||||
{
|
||||
@@ -75,7 +75,7 @@ namespace Netch.Servers.Trojan
|
||||
if (!match.Success)
|
||||
throw new FormatException();
|
||||
|
||||
data.Password = match.Groups["psk"].Value;
|
||||
data.Password = HttpUtility.UrlDecode(match.Groups["psk"].Value);
|
||||
data.Hostname = match.Groups["server"].Value;
|
||||
data.Port = ushort.Parse(match.Groups["port"].Value);
|
||||
|
||||
|
||||
@@ -1,57 +1,21 @@
|
||||
#nullable disable
|
||||
using System.Collections.Generic;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace Netch.Servers.V2ray.Models
|
||||
{
|
||||
public class V2rayConfig
|
||||
public struct V2rayConfig
|
||||
{
|
||||
public List<Inbounds> inbounds { get; } = new();
|
||||
public object[] inbounds { get; set; }
|
||||
|
||||
public List<Outbounds> outbounds { get; } = new();
|
||||
|
||||
public Routing routing { get; } = new();
|
||||
public Outbound[] outbounds { get; set; }
|
||||
}
|
||||
|
||||
public class Inbounds
|
||||
{
|
||||
public string tag { get; set; }
|
||||
|
||||
public ushort port { get; set; }
|
||||
|
||||
public string listen { get; set; }
|
||||
|
||||
public string protocol { get; set; }
|
||||
|
||||
public Sniffing sniffing { get; set; }
|
||||
|
||||
public Inboundsettings settings { get; set; }
|
||||
|
||||
public StreamSettings streamSettings { get; set; }
|
||||
}
|
||||
|
||||
public class Inboundsettings
|
||||
{
|
||||
public string auth { get; set; }
|
||||
|
||||
public bool udp { get; set; }
|
||||
|
||||
public string ip { get; set; }
|
||||
|
||||
public string address { get; set; }
|
||||
|
||||
public List<UsersItem> clients { get; set; }
|
||||
|
||||
public string decryption { get; set; }
|
||||
}
|
||||
|
||||
public class UsersItem
|
||||
public class User
|
||||
{
|
||||
public string id { get; set; }
|
||||
|
||||
public int alterId { get; set; }
|
||||
|
||||
public string email { get; set; }
|
||||
|
||||
public string security { get; set; }
|
||||
|
||||
public string encryption { get; set; }
|
||||
@@ -59,33 +23,22 @@ namespace Netch.Servers.V2ray.Models
|
||||
public string flow { get; set; }
|
||||
}
|
||||
|
||||
public class Sniffing
|
||||
public class Outbound
|
||||
{
|
||||
public bool enabled { get; set; }
|
||||
|
||||
public List<string> destOverride { get; set; }
|
||||
}
|
||||
|
||||
public class Outbounds
|
||||
{
|
||||
public string tag { get; set; }
|
||||
|
||||
public string protocol { get; set; }
|
||||
|
||||
public Outboundsettings settings { get; set; }
|
||||
public OutboundConfiguration settings { get; set; }
|
||||
|
||||
public StreamSettings streamSettings { get; set; }
|
||||
|
||||
public Mux mux { get; set; }
|
||||
}
|
||||
|
||||
public class Outboundsettings
|
||||
public class OutboundConfiguration
|
||||
{
|
||||
public List<VnextItem> vnext { get; set; }
|
||||
public VnextItem[] vnext { get; set; }
|
||||
|
||||
public List<ServersItem> servers { get; set; }
|
||||
|
||||
public Response response { get; set; }
|
||||
public object[] servers { get; set; }
|
||||
}
|
||||
|
||||
public class VnextItem
|
||||
@@ -94,35 +47,7 @@ namespace Netch.Servers.V2ray.Models
|
||||
|
||||
public ushort port { get; set; }
|
||||
|
||||
public List<UsersItem> users { get; set; }
|
||||
}
|
||||
|
||||
public class ServersItem
|
||||
{
|
||||
public string email { get; set; }
|
||||
|
||||
public string address { get; set; }
|
||||
|
||||
public string method { get; set; }
|
||||
|
||||
public bool ota { get; set; }
|
||||
|
||||
public string password { get; set; }
|
||||
|
||||
public ushort port { get; set; }
|
||||
|
||||
public int level { get; set; }
|
||||
|
||||
public List<SocksUsersItem> users { get; set; }
|
||||
}
|
||||
|
||||
public class SocksUsersItem
|
||||
{
|
||||
public string user { get; set; }
|
||||
|
||||
public string pass { get; set; }
|
||||
|
||||
public int level { get; set; }
|
||||
public User[] users { get; set; }
|
||||
}
|
||||
|
||||
public class Mux
|
||||
@@ -132,38 +57,6 @@ namespace Netch.Servers.V2ray.Models
|
||||
public int concurrency { get; set; }
|
||||
}
|
||||
|
||||
public class Response
|
||||
{
|
||||
public string type { get; set; }
|
||||
}
|
||||
|
||||
public class Dns
|
||||
{
|
||||
public List<string> servers { get; set; }
|
||||
}
|
||||
|
||||
public class RulesItem
|
||||
{
|
||||
public string type { get; set; }
|
||||
|
||||
public string port { get; set; }
|
||||
|
||||
public List<string> inboundTag { get; set; }
|
||||
|
||||
public string outboundTag { get; set; }
|
||||
|
||||
public List<string> ip { get; set; }
|
||||
|
||||
public List<string> domain { get; set; }
|
||||
}
|
||||
|
||||
public class Routing
|
||||
{
|
||||
public string domainStrategy { get; set; }
|
||||
|
||||
public List<RulesItem> rules { get; } = new();
|
||||
}
|
||||
|
||||
public class StreamSettings
|
||||
{
|
||||
public string network { get; set; }
|
||||
@@ -183,8 +76,12 @@ namespace Netch.Servers.V2ray.Models
|
||||
public QuicSettings quicSettings { get; set; }
|
||||
|
||||
public TlsSettings xtlsSettings { get; set; }
|
||||
|
||||
public GrpcSettings grpcSettings { get; set; }
|
||||
}
|
||||
|
||||
#region Transport
|
||||
|
||||
public class TlsSettings
|
||||
{
|
||||
public bool allowInsecure { get; set; }
|
||||
@@ -194,40 +91,14 @@ namespace Netch.Servers.V2ray.Models
|
||||
|
||||
public class TcpSettings
|
||||
{
|
||||
public Header header { get; set; }
|
||||
public object header { get; set; }
|
||||
}
|
||||
|
||||
public class Header
|
||||
public class WsSettings
|
||||
{
|
||||
public string type { get; set; }
|
||||
public string path { get; set; }
|
||||
|
||||
public TCPRequest request { get; set; }
|
||||
|
||||
public object response { get; set; }
|
||||
}
|
||||
|
||||
public class TCPRequest
|
||||
{
|
||||
public TCPRequestHeaders headers { get; set; }
|
||||
|
||||
public string method { get; set; } = "GET";
|
||||
|
||||
public string path { get; set; } = "/";
|
||||
|
||||
public string version { get; set; } = "1.1";
|
||||
}
|
||||
|
||||
public class TCPRequestHeaders
|
||||
{
|
||||
//public string User_Agent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36";
|
||||
|
||||
public string Accept_Encoding { get; set; } = "gzip, deflate";
|
||||
|
||||
public string Connection { get; set; } = "keep-alive";
|
||||
|
||||
public string Host { get; set; }
|
||||
|
||||
public string Pragma { get; set; } = "no-cache";
|
||||
public object headers { get; set; }
|
||||
}
|
||||
|
||||
public class KcpSettings
|
||||
@@ -246,28 +117,16 @@ namespace Netch.Servers.V2ray.Models
|
||||
|
||||
public int writeBufferSize { get; set; }
|
||||
|
||||
public Header header { get; set; }
|
||||
public object header { get; set; }
|
||||
|
||||
public string seed { get; set; }
|
||||
}
|
||||
|
||||
public class WsSettings
|
||||
{
|
||||
public string path { get; set; }
|
||||
|
||||
public Headers headers { get; set; }
|
||||
}
|
||||
|
||||
public class Headers
|
||||
{
|
||||
public string Host { get; set; }
|
||||
}
|
||||
|
||||
public class HttpSettings
|
||||
{
|
||||
public string path { get; set; }
|
||||
|
||||
public List<string> host { get; set; }
|
||||
public string[] host { get; set; }
|
||||
}
|
||||
|
||||
public class QuicSettings
|
||||
@@ -276,6 +135,15 @@ namespace Netch.Servers.V2ray.Models
|
||||
|
||||
public string key { get; set; }
|
||||
|
||||
public Header header { get; set; }
|
||||
public object header { get; set; }
|
||||
}
|
||||
|
||||
public class GrpcSettings
|
||||
{
|
||||
public string serviceName { get; set; }
|
||||
|
||||
public bool multiMode { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Netch.Servers.V2ray.Models
|
||||
namespace Netch.Servers.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用 v2rayN 定义的 VMess 链接格式
|
||||
@@ -6,39 +6,9 @@
|
||||
public class V2rayNSharing
|
||||
{
|
||||
/// <summary>
|
||||
/// 地址
|
||||
/// 链接版本
|
||||
/// </summary>
|
||||
public string add { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 额外 ID
|
||||
/// </summary>
|
||||
public int aid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 伪装域名(HTTP,WS)
|
||||
/// </summary>
|
||||
public string? host { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 用户 ID
|
||||
/// </summary>
|
||||
public string id { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 传输协议
|
||||
/// </summary>
|
||||
public string net { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 伪装路径
|
||||
/// </summary>
|
||||
public string? path { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 端口
|
||||
/// </summary>
|
||||
public ushort port { get; set; }
|
||||
public int v { get; set; } = 2;
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
@@ -46,9 +16,34 @@
|
||||
public string ps { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用 TLS
|
||||
/// 地址
|
||||
/// </summary>
|
||||
public string tls { get; set; } = string.Empty;
|
||||
public string add { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 端口
|
||||
/// </summary>
|
||||
public ushort port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户 ID
|
||||
/// </summary>
|
||||
public string id { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 额外 ID
|
||||
/// </summary>
|
||||
public int aid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 加密方式 (security)
|
||||
/// </summary>
|
||||
public string scy { get; set; } = "auto";
|
||||
|
||||
/// <summary>
|
||||
/// 传输协议
|
||||
/// </summary>
|
||||
public string net { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 伪装类型
|
||||
@@ -56,8 +51,23 @@
|
||||
public string type { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 链接版本
|
||||
/// 伪装域名(HTTP,WS)
|
||||
/// </summary>
|
||||
public int v { get; set; } = 2;
|
||||
public string host { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 伪装路径/服务名称
|
||||
/// </summary>
|
||||
public string path { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用 TLS
|
||||
/// </summary>
|
||||
public string tls { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// serverName
|
||||
/// </summary>
|
||||
public string sni { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,344 +1,263 @@
|
||||
using Netch.Models;
|
||||
using Netch.Servers.V2ray.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Netch.Enums;
|
||||
using Netch.Utils;
|
||||
using V2rayConfig = Netch.Servers.V2ray.Models.V2rayConfig;
|
||||
|
||||
namespace Netch.Servers.V2ray.Utils
|
||||
namespace Netch.Servers.Utils
|
||||
{
|
||||
public static class V2rayConfigUtils
|
||||
{
|
||||
public static string GenerateClientConfig(Server server, Mode mode)
|
||||
public static V2rayConfig GenerateClientConfig(Server server)
|
||||
{
|
||||
var v2rayConfig = new V2rayConfig();
|
||||
|
||||
inbound(server, ref v2rayConfig);
|
||||
|
||||
routing(server, mode, ref v2rayConfig);
|
||||
|
||||
outbound(server, mode, ref v2rayConfig);
|
||||
|
||||
return JsonSerializer.Serialize(v2rayConfig, Global.NewDefaultJsonSerializerOptions);
|
||||
}
|
||||
|
||||
private static void inbound(Server server, ref V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
var v2rayConfig = new V2rayConfig
|
||||
{
|
||||
var inbound = new Inbounds
|
||||
inbounds = new object[]
|
||||
{
|
||||
port = Global.Settings.Socks5LocalPort,
|
||||
protocol = "socks",
|
||||
listen = Global.Settings.LocalAddress,
|
||||
settings = new Inboundsettings
|
||||
new
|
||||
{
|
||||
udp = true
|
||||
}
|
||||
};
|
||||
|
||||
v2rayConfig.inbounds.Add(inbound);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static void routing(Server server, Mode mode, ref V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var directRuleObject = new RulesItem
|
||||
{
|
||||
type = "field",
|
||||
ip = new List<string>(),
|
||||
domain = new List<string>(),
|
||||
outboundTag = "direct"
|
||||
};
|
||||
|
||||
var blockRuleObject = new RulesItem
|
||||
{
|
||||
type = "field",
|
||||
ip = new List<string>(),
|
||||
domain = new List<string>(),
|
||||
outboundTag = "block"
|
||||
};
|
||||
|
||||
if (mode.Type is ModeType.Process or ModeType.ProxyRuleIPs or ModeType.BypassRuleIPs)
|
||||
blockRuleObject.ip.Add("geoip:private");
|
||||
|
||||
static bool CheckRuleItem(ref RulesItem rulesItem)
|
||||
{
|
||||
bool ipResult, domainResult;
|
||||
if (!(ipResult = rulesItem.ip?.Any() ?? false))
|
||||
rulesItem.ip = null;
|
||||
|
||||
if (!(domainResult = rulesItem.domain?.Any() ?? false))
|
||||
rulesItem.domain = null;
|
||||
|
||||
return ipResult || domainResult;
|
||||
}
|
||||
|
||||
if (CheckRuleItem(ref directRuleObject))
|
||||
v2rayConfig.routing.rules.Add(directRuleObject);
|
||||
|
||||
if (CheckRuleItem(ref blockRuleObject))
|
||||
v2rayConfig.routing.rules.Add(blockRuleObject);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static void outbound(Server server, Mode mode, ref V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var outbound = new Outbounds
|
||||
{
|
||||
settings = new Outboundsettings(),
|
||||
mux = new Mux(),
|
||||
streamSettings = new StreamSettings
|
||||
{
|
||||
network = "tcp"
|
||||
}
|
||||
};
|
||||
|
||||
switch (server)
|
||||
{
|
||||
case Socks5.Socks5 socks5:
|
||||
port = Global.Settings.Socks5LocalPort,
|
||||
protocol = "socks",
|
||||
listen = Global.Settings.LocalAddress,
|
||||
settings = new
|
||||
{
|
||||
outbound.settings.servers = new List<ServersItem>
|
||||
{
|
||||
new()
|
||||
{
|
||||
users = socks5.Auth()
|
||||
? new List<SocksUsersItem>
|
||||
{
|
||||
new()
|
||||
{
|
||||
user = socks5.Username,
|
||||
pass = socks5.Password,
|
||||
level = 1
|
||||
}
|
||||
}
|
||||
: null,
|
||||
address = server.AutoResolveHostname(),
|
||||
port = server.Port
|
||||
}
|
||||
};
|
||||
|
||||
outbound.mux.enabled = false;
|
||||
outbound.mux.concurrency = -1;
|
||||
outbound.protocol = "socks";
|
||||
break;
|
||||
udp = true
|
||||
}
|
||||
case VLESS.VLESS vless:
|
||||
{
|
||||
var vnextItem = new VnextItem
|
||||
{
|
||||
users = new List<UsersItem>(),
|
||||
address = server.AutoResolveHostname(),
|
||||
port = server.Port
|
||||
};
|
||||
|
||||
outbound.settings.vnext = new List<VnextItem> { vnextItem };
|
||||
|
||||
var usersItem = new UsersItem
|
||||
{
|
||||
id = vless.UserID,
|
||||
alterId = 0,
|
||||
flow = string.Empty,
|
||||
encryption = vless.EncryptMethod
|
||||
};
|
||||
|
||||
vnextItem.users.Add(usersItem);
|
||||
|
||||
var streamSettings = outbound.streamSettings;
|
||||
boundStreamSettings(vless, ref streamSettings);
|
||||
|
||||
if (vless.TLSSecureType == "xtls")
|
||||
{
|
||||
usersItem.flow = string.IsNullOrEmpty(vless.Flow) ? "xtls-rprx-origin" : vless.Flow;
|
||||
|
||||
outbound.mux.enabled = false;
|
||||
outbound.mux.concurrency = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
outbound.mux.enabled = vless.UseMux ?? Global.Settings.V2RayConfig.UseMux;
|
||||
outbound.mux.concurrency = vless.UseMux ?? Global.Settings.V2RayConfig.UseMux ? 8 : -1;
|
||||
}
|
||||
|
||||
outbound.protocol = "vless";
|
||||
outbound.settings.servers = null;
|
||||
break;
|
||||
}
|
||||
case VMess.VMess vmess:
|
||||
{
|
||||
var vnextItem = new VnextItem
|
||||
{
|
||||
users = new List<UsersItem>(),
|
||||
address = server.AutoResolveHostname(),
|
||||
port = server.Port
|
||||
};
|
||||
|
||||
outbound.settings.vnext = new List<VnextItem> { vnextItem };
|
||||
|
||||
var usersItem = new UsersItem
|
||||
{
|
||||
id = vmess.UserID,
|
||||
alterId = vmess.AlterID,
|
||||
security = vmess.EncryptMethod
|
||||
};
|
||||
|
||||
vnextItem.users.Add(usersItem);
|
||||
|
||||
var streamSettings = outbound.streamSettings;
|
||||
boundStreamSettings(vmess, ref streamSettings);
|
||||
|
||||
outbound.mux.enabled = vmess.UseMux ?? Global.Settings.V2RayConfig.UseMux;
|
||||
outbound.mux.concurrency = vmess.UseMux ?? Global.Settings.V2RayConfig.UseMux ? 8 : -1;
|
||||
outbound.protocol = "vmess";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
v2rayConfig.outbounds.AddRange(new[]
|
||||
{
|
||||
outbound,
|
||||
new()
|
||||
{
|
||||
tag = "direct", protocol = "freedom"
|
||||
},
|
||||
new()
|
||||
{
|
||||
tag = "block", protocol = "blackhole"
|
||||
}
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static void boundStreamSettings(VMess.VMess server, ref StreamSettings streamSettings)
|
||||
{
|
||||
try
|
||||
{
|
||||
streamSettings.network = server.TransferProtocol;
|
||||
|
||||
if ((streamSettings.security = server.TLSSecureType) != "none")
|
||||
{
|
||||
var tlsSettings = new TlsSettings
|
||||
{
|
||||
allowInsecure = Global.Settings.V2RayConfig.AllowInsecure,
|
||||
serverName = !string.IsNullOrWhiteSpace(server.Host) ? server.Host : null
|
||||
};
|
||||
|
||||
switch (server.TLSSecureType)
|
||||
{
|
||||
case "tls":
|
||||
streamSettings.tlsSettings = tlsSettings;
|
||||
break;
|
||||
case "xtls":
|
||||
streamSettings.xtlsSettings = tlsSettings;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
switch (server.TransferProtocol)
|
||||
v2rayConfig.outbounds = new[] { outbound(server) };
|
||||
|
||||
return v2rayConfig;
|
||||
}
|
||||
|
||||
private static Outbound outbound(Server server)
|
||||
{
|
||||
var outbound = new Outbound
|
||||
{
|
||||
settings = new OutboundConfiguration(),
|
||||
mux = new Mux()
|
||||
};
|
||||
|
||||
switch (server)
|
||||
{
|
||||
case Socks5 socks5:
|
||||
{
|
||||
case "kcp":
|
||||
var kcpSettings = new KcpSettings
|
||||
outbound.protocol = "socks";
|
||||
outbound.settings.servers = new object[]
|
||||
{
|
||||
new
|
||||
{
|
||||
mtu = Global.Settings.V2RayConfig.KcpConfig.mtu,
|
||||
tti = Global.Settings.V2RayConfig.KcpConfig.tti,
|
||||
uplinkCapacity = Global.Settings.V2RayConfig.KcpConfig.uplinkCapacity,
|
||||
downlinkCapacity = Global.Settings.V2RayConfig.KcpConfig.downlinkCapacity,
|
||||
congestion = Global.Settings.V2RayConfig.KcpConfig.congestion,
|
||||
readBufferSize = Global.Settings.V2RayConfig.KcpConfig.readBufferSize,
|
||||
writeBufferSize = Global.Settings.V2RayConfig.KcpConfig.writeBufferSize,
|
||||
header = new Header
|
||||
{
|
||||
type = server.FakeType
|
||||
},
|
||||
seed = !string.IsNullOrWhiteSpace(server.Path) ? server.Path : null
|
||||
};
|
||||
|
||||
streamSettings.kcpSettings = kcpSettings;
|
||||
break;
|
||||
case "ws":
|
||||
var wsSettings = new WsSettings
|
||||
{
|
||||
headers = !string.IsNullOrWhiteSpace(server.Host) ? new Headers { Host = server.Host } : null,
|
||||
path = !string.IsNullOrWhiteSpace(server.Path) ? server.Path : null
|
||||
};
|
||||
|
||||
streamSettings.wsSettings = wsSettings;
|
||||
break;
|
||||
case "h2":
|
||||
var httpSettings = new HttpSettings
|
||||
{
|
||||
host = new List<string>
|
||||
{
|
||||
string.IsNullOrWhiteSpace(server.Host) ? server.Hostname : server.Host!
|
||||
},
|
||||
path = server.Path
|
||||
};
|
||||
|
||||
streamSettings.httpSettings = httpSettings;
|
||||
break;
|
||||
case "quic":
|
||||
var quicSettings = new QuicSettings
|
||||
{
|
||||
security = server.QUICSecure,
|
||||
key = server.QUICSecret,
|
||||
header = new Header
|
||||
{
|
||||
type = server.FakeType
|
||||
}
|
||||
};
|
||||
|
||||
if (server.TLSSecureType != "none")
|
||||
// tls or xtls
|
||||
streamSettings.tlsSettings.serverName = server.Hostname;
|
||||
|
||||
streamSettings.quicSettings = quicSettings;
|
||||
break;
|
||||
default:
|
||||
if (server.FakeType == "http")
|
||||
{
|
||||
var tcpSettings = new TcpSettings
|
||||
{
|
||||
header = new Header
|
||||
address = server.AutoResolveHostname(),
|
||||
port = server.Port,
|
||||
users = socks5.Auth()
|
||||
? new[]
|
||||
{
|
||||
type = server.FakeType,
|
||||
request = new TCPRequest
|
||||
new
|
||||
{
|
||||
path = string.IsNullOrWhiteSpace(server.Path) ? "/" : server.Path,
|
||||
headers = new TCPRequestHeaders
|
||||
{
|
||||
Host = string.IsNullOrWhiteSpace(server.Host) ? server.Hostname : server.Host
|
||||
}
|
||||
user = socks5.Username,
|
||||
pass = socks5.Password,
|
||||
level = 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
streamSettings.tcpSettings = tcpSettings;
|
||||
: null
|
||||
}
|
||||
};
|
||||
|
||||
outbound.mux.enabled = false;
|
||||
outbound.mux.concurrency = -1;
|
||||
break;
|
||||
}
|
||||
case VLESS vless:
|
||||
{
|
||||
outbound.protocol = "vless";
|
||||
outbound.settings.vnext = new[]
|
||||
{
|
||||
new VnextItem
|
||||
{
|
||||
address = server.AutoResolveHostname(),
|
||||
port = server.Port,
|
||||
users = new[]
|
||||
{
|
||||
new User
|
||||
{
|
||||
id = vless.UserID,
|
||||
flow = vless.Flow.ValueOrDefault(),
|
||||
encryption = vless.EncryptMethod
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
outbound.streamSettings = boundStreamSettings(vless);
|
||||
|
||||
if (vless.TLSSecureType == "xtls")
|
||||
{
|
||||
outbound.mux.enabled = false;
|
||||
outbound.mux.concurrency = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
outbound.mux.enabled = vless.UseMux ?? Global.Settings.V2RayConfig.UseMux;
|
||||
outbound.mux.concurrency = vless.UseMux ?? Global.Settings.V2RayConfig.UseMux ? 8 : -1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case VMess vmess:
|
||||
{
|
||||
outbound.protocol = "vmess";
|
||||
outbound.settings.vnext = new[]
|
||||
{
|
||||
new VnextItem
|
||||
{
|
||||
address = server.AutoResolveHostname(),
|
||||
port = server.Port,
|
||||
users = new[]
|
||||
{
|
||||
new User
|
||||
{
|
||||
id = vmess.UserID,
|
||||
alterId = vmess.AlterID,
|
||||
security = vmess.EncryptMethod
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
outbound.streamSettings = boundStreamSettings(vmess);
|
||||
|
||||
outbound.mux.enabled = vmess.UseMux ?? Global.Settings.V2RayConfig.UseMux;
|
||||
outbound.mux.concurrency = vmess.UseMux ?? Global.Settings.V2RayConfig.UseMux ? 8 : -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return outbound;
|
||||
}
|
||||
|
||||
private static StreamSettings boundStreamSettings(VMess server)
|
||||
{
|
||||
// https://xtls.github.io/config/transports
|
||||
|
||||
var streamSettings = new StreamSettings
|
||||
{
|
||||
network = server.TransferProtocol,
|
||||
security = server.TLSSecureType
|
||||
};
|
||||
|
||||
if (server.TLSSecureType != "none")
|
||||
{
|
||||
var tlsSettings = new TlsSettings
|
||||
{
|
||||
allowInsecure = Global.Settings.V2RayConfig.AllowInsecure,
|
||||
serverName = server.ServerName.ValueOrDefault() ?? server.Hostname
|
||||
};
|
||||
|
||||
switch (server.TLSSecureType)
|
||||
{
|
||||
case "tls":
|
||||
streamSettings.tlsSettings = tlsSettings;
|
||||
break;
|
||||
case "xtls":
|
||||
streamSettings.xtlsSettings = tlsSettings;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
||||
switch (server.TransferProtocol)
|
||||
{
|
||||
// ignored
|
||||
case "tcp":
|
||||
|
||||
streamSettings.tcpSettings = new TcpSettings
|
||||
{
|
||||
header = new
|
||||
{
|
||||
type = server.FakeType,
|
||||
request = server.FakeType switch
|
||||
{
|
||||
"none" => null,
|
||||
"http" => new
|
||||
{
|
||||
path = server.Path.SplitOrDefault(),
|
||||
headers = new
|
||||
{
|
||||
Host = server.Host.SplitOrDefault()
|
||||
}
|
||||
},
|
||||
_ => throw new MessageException($"Invalid tcp type {server.FakeType}")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
break;
|
||||
case "ws":
|
||||
|
||||
streamSettings.wsSettings = new WsSettings
|
||||
{
|
||||
path = server.Path.ValueOrDefault(),
|
||||
headers = new
|
||||
{
|
||||
Host = server.Host.ValueOrDefault()
|
||||
}
|
||||
};
|
||||
|
||||
break;
|
||||
case "kcp":
|
||||
|
||||
streamSettings.kcpSettings = new KcpSettings
|
||||
{
|
||||
mtu = Global.Settings.V2RayConfig.KcpConfig.mtu,
|
||||
tti = Global.Settings.V2RayConfig.KcpConfig.tti,
|
||||
uplinkCapacity = Global.Settings.V2RayConfig.KcpConfig.uplinkCapacity,
|
||||
downlinkCapacity = Global.Settings.V2RayConfig.KcpConfig.downlinkCapacity,
|
||||
congestion = Global.Settings.V2RayConfig.KcpConfig.congestion,
|
||||
readBufferSize = Global.Settings.V2RayConfig.KcpConfig.readBufferSize,
|
||||
writeBufferSize = Global.Settings.V2RayConfig.KcpConfig.writeBufferSize,
|
||||
header = new
|
||||
{
|
||||
type = server.FakeType
|
||||
},
|
||||
seed = server.Path.ValueOrDefault()
|
||||
};
|
||||
|
||||
break;
|
||||
case "h2":
|
||||
|
||||
streamSettings.httpSettings = new HttpSettings
|
||||
{
|
||||
host = server.Host.SplitOrDefault(),
|
||||
path = server.Path.ValueOrDefault()
|
||||
};
|
||||
|
||||
break;
|
||||
case "quic":
|
||||
|
||||
streamSettings.quicSettings = new QuicSettings
|
||||
{
|
||||
security = server.QUICSecure,
|
||||
key = server.QUICSecret,
|
||||
header = new
|
||||
{
|
||||
type = server.FakeType
|
||||
}
|
||||
};
|
||||
|
||||
break;
|
||||
case "grpc":
|
||||
|
||||
streamSettings.grpcSettings = new GrpcSettings
|
||||
{
|
||||
serviceName = server.Path,
|
||||
multiMode = server.FakeType == "multi"
|
||||
};
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new MessageException($"transfer protocol \"{server.TransferProtocol}\" not implemented yet");
|
||||
}
|
||||
|
||||
return streamSettings;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using Netch.Controllers;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.V2ray.Utils;
|
||||
using Netch.Servers.Utils;
|
||||
|
||||
namespace Netch.Servers.V2ray
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class V2rayController : Guard, IServerController
|
||||
{
|
||||
public override string MainFile { get; protected set; } = "xray.exe";
|
||||
public V2rayController() : base("xray.exe")
|
||||
{
|
||||
if (!Global.Settings.V2RayConfig.XrayCone)
|
||||
Instance.StartInfo.Environment["XRAY_CONE_DISABLED"] = "true";
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> StartedKeywords { get; set; } = new[] { "started" };
|
||||
protected override IEnumerable<string> StartedKeywords => new[] { "started" };
|
||||
|
||||
protected override IEnumerable<string> StoppedKeywords { get; set; } = new[] { "config file not readable", "failed to" };
|
||||
protected override IEnumerable<string> FailedKeywords => new[] { "config file not readable", "failed to" };
|
||||
|
||||
public override string Name { get; } = "Xray";
|
||||
public override string Name => "Xray";
|
||||
|
||||
public ushort? Socks5LocalPort { get; set; }
|
||||
|
||||
public string? LocalAddress { get; set; }
|
||||
|
||||
public virtual void Start(in Server s, in Mode mode)
|
||||
public virtual Socks5 Start(in Server s)
|
||||
{
|
||||
File.WriteAllText("data\\last.json", V2rayConfigUtils.GenerateClientConfig(s, mode));
|
||||
StartInstanceAuto("-config ..\\data\\last.json");
|
||||
}
|
||||
using (var fileStream = new FileStream(Constants.TempConfig, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
JsonSerializer.SerializeAsync(fileStream, V2rayConfigUtils.GenerateClientConfig(s), Global.NewCustomJsonSerializerOptions()).Wait();
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
StopInstance();
|
||||
}
|
||||
|
||||
protected override void InitInstance(string argument)
|
||||
{
|
||||
base.InitInstance(argument);
|
||||
if (!Global.Settings.V2RayConfig.XrayCone)
|
||||
Instance!.StartInfo.Environment["XRAY_CONE_DISABLED"] = "true";
|
||||
StartGuard("-config ..\\data\\last.json");
|
||||
return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), s.Hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Servers.V2ray
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public static class V2rayUtils
|
||||
{
|
||||
public static IEnumerable<Server> ParseVUri(string text)
|
||||
{
|
||||
var scheme = ShareLink.GetUriScheme(text).ToLower();
|
||||
var server = scheme switch { "vmess" => new VMess.VMess(), "vless" => new VLESS.VLESS(), _ => throw new ArgumentOutOfRangeException() };
|
||||
var server = scheme switch { "vmess" => new VMess(), "vless" => new VLESS(), _ => throw new ArgumentOutOfRangeException() };
|
||||
if (text.Contains("#"))
|
||||
{
|
||||
server.Remark = Uri.UnescapeDataString(text.Split('#')[1]);
|
||||
@@ -47,14 +47,18 @@ namespace Netch.Servers.V2ray
|
||||
server.QUICSecret = parameter.Get("key") ?? "";
|
||||
server.FakeType = parameter.Get("headerType") ?? "none";
|
||||
break;
|
||||
case "grpc":
|
||||
server.FakeType = parameter.Get("mode") ?? "gun";
|
||||
server.Path = parameter.Get("serviceName") ?? "";
|
||||
break;
|
||||
}
|
||||
|
||||
server.TLSSecureType = parameter.Get("security") ?? "none";
|
||||
if (server.TLSSecureType != "none")
|
||||
{
|
||||
server.Host = parameter.Get("sni") ?? "";
|
||||
server.ServerName = parameter.Get("sni") ?? "";
|
||||
if (server.TLSSecureType == "xtls")
|
||||
((VLESS.VLESS)server).Flow = parameter.Get("flow") ?? "";
|
||||
((VLESS)server).Flow = parameter.Get("flow") ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +77,7 @@ namespace Netch.Servers.V2ray
|
||||
public static string GetVShareLink(Server s, string scheme = "vmess")
|
||||
{
|
||||
// https://github.com/XTLS/Xray-core/issues/91
|
||||
var server = (VMess.VMess)s;
|
||||
var server = (VMess)s;
|
||||
var parameter = new Dictionary<string, string>();
|
||||
// protocol-specific fields
|
||||
parameter.Add("type", server.TransferProtocol);
|
||||
@@ -93,13 +97,13 @@ namespace Netch.Servers.V2ray
|
||||
|
||||
break;
|
||||
case "ws":
|
||||
parameter.Add("path", Uri.EscapeDataString(server.Path.IsNullOrWhiteSpace() ? "/" : server.Path!));
|
||||
parameter.Add("path", Uri.EscapeDataString(server.Path.ValueOrDefault() ?? "/"));
|
||||
if (!server.Host.IsNullOrWhiteSpace())
|
||||
parameter.Add("host", Uri.EscapeDataString(server.Host!));
|
||||
|
||||
break;
|
||||
case "h2":
|
||||
parameter.Add("path", Uri.EscapeDataString(server.Path.IsNullOrWhiteSpace() ? "/" : server.Path!));
|
||||
parameter.Add("path", Uri.EscapeDataString(server.Path.ValueOrDefault() ?? "/"));
|
||||
if (!server.Host.IsNullOrWhiteSpace())
|
||||
parameter.Add("host", Uri.EscapeDataString(server.Host!));
|
||||
|
||||
@@ -109,12 +113,19 @@ namespace Netch.Servers.V2ray
|
||||
{
|
||||
parameter.Add("quicSecurity", server.QUICSecure);
|
||||
parameter.Add("key", server.QUICSecret!);
|
||||
// TODO Import and Create null value Check
|
||||
}
|
||||
|
||||
if (server.FakeType != "none")
|
||||
parameter.Add("headerType", server.FakeType);
|
||||
|
||||
break;
|
||||
case "grpc":
|
||||
if (!string.IsNullOrEmpty(server.Path))
|
||||
parameter.Add("serviceName", server.Path);
|
||||
|
||||
if (server.FakeType is "gun" or "multi")
|
||||
parameter.Add("mode", server.FakeType);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -127,14 +138,14 @@ namespace Netch.Servers.V2ray
|
||||
|
||||
if (server.TLSSecureType == "xtls")
|
||||
{
|
||||
var flow = ((VLESS.VLESS)server).Flow;
|
||||
var flow = ((VLESS)server).Flow;
|
||||
if (!flow.IsNullOrWhiteSpace())
|
||||
parameter.Add("flow", flow!.Replace("-udp443", ""));
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
$"{scheme}://{server.UserID}@{server.Hostname}:{server.Port}?{string.Join("&", parameter.Select(p => $"{p.Key}={p.Value}"))}{(server.Remark.IsNullOrWhiteSpace() ? "" : $"#{Uri.EscapeDataString(server.Remark)}")}";
|
||||
$"{scheme}://{server.UserID}@{server.Hostname}:{server.Port}?{string.Join("&", parameter.Select(p => $"{p.Key}={p.Value}"))}{(!server.Remark.IsNullOrWhiteSpace() ? $"#{Uri.EscapeDataString(server.Remark)}" : "")}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using Netch.Servers.VMess;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Netch.Servers.VLESS
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class VLESS : VMess.VMess
|
||||
public class VLESS : VMess
|
||||
{
|
||||
public override string Type { get; } = "VLESS";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Netch.Forms;
|
||||
using System.Collections.Generic;
|
||||
using Netch.Forms;
|
||||
|
||||
namespace Netch.Servers.VLESS.VLESSForm
|
||||
namespace Netch.Servers.VLESSForm
|
||||
{
|
||||
internal class VLESSForm : ServerForm
|
||||
{
|
||||
@@ -9,6 +9,7 @@ namespace Netch.Servers.VLESS.VLESSForm
|
||||
{
|
||||
server ??= new VLESS();
|
||||
Server = server;
|
||||
CreateTextBox("Sni", "ServerName(Sni)", s => true, s => server.ServerName = s, server.ServerName);
|
||||
CreateTextBox("UUID", "UUID", s => true, s => server.UserID = s, server.UserID);
|
||||
CreateTextBox("EncryptMethod",
|
||||
"Encrypt Method",
|
||||
|
||||
@@ -2,9 +2,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.V2ray;
|
||||
using Netch.Servers;
|
||||
|
||||
namespace Netch.Servers.VLESS
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class VLESSUtil : IServerUtil
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Netch.Forms;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Netch.Forms;
|
||||
|
||||
namespace Netch.Servers.VMess.Form
|
||||
namespace Netch.Servers.Form
|
||||
{
|
||||
public class VMessForm : ServerForm
|
||||
{
|
||||
@@ -9,6 +9,7 @@ namespace Netch.Servers.VMess.Form
|
||||
{
|
||||
server ??= new VMess();
|
||||
Server = server;
|
||||
CreateTextBox("Sni", "ServerName(Sni)", s => true, s => server.ServerName = s, server.ServerName);
|
||||
CreateTextBox("UserId", "User ID", s => true, s => server.UserID = s, server.UserID);
|
||||
CreateTextBox("AlterId", "Alter ID", s => int.TryParse(s, out _), s => server.AlterID = int.Parse(s), server.AlterID.ToString(), 76);
|
||||
CreateComboBox("EncryptMethod", "Encrypt Method", VMessGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Netch.Models;
|
||||
using System.Collections.Generic;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Servers.VMess
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class VMess : Server
|
||||
{
|
||||
@@ -9,6 +9,27 @@ namespace Netch.Servers.VMess
|
||||
|
||||
public override string Type { get; } = "VMess";
|
||||
|
||||
public override string MaskedData()
|
||||
{
|
||||
var maskedData = $"{EncryptMethod} + {TransferProtocol} + {FakeType}";
|
||||
switch (TransferProtocol)
|
||||
{
|
||||
case "tcp":
|
||||
case "ws":
|
||||
maskedData += $" + {TLSSecureType}";
|
||||
break;
|
||||
case "quic":
|
||||
maskedData += $" + {QUICSecure}";
|
||||
break;
|
||||
case "grpc":
|
||||
break;
|
||||
case "kcp":
|
||||
break;
|
||||
}
|
||||
|
||||
return maskedData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户 ID
|
||||
/// </summary>
|
||||
@@ -72,7 +93,9 @@ namespace Netch.Servers.VMess
|
||||
/// <summary>
|
||||
/// Mux 多路复用
|
||||
/// </summary>
|
||||
public bool? UseMux { get; set; } = false;
|
||||
public bool? UseMux { get; set; }
|
||||
|
||||
public string? ServerName { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class VMessGlobal
|
||||
@@ -101,7 +124,8 @@ namespace Netch.Servers.VMess
|
||||
"kcp",
|
||||
"ws",
|
||||
"h2",
|
||||
"quic"
|
||||
"quic",
|
||||
"grpc"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -115,7 +139,9 @@ namespace Netch.Servers.VMess
|
||||
"utp",
|
||||
"wechat-video",
|
||||
"dtls",
|
||||
"wireguard"
|
||||
"wireguard",
|
||||
"gun",
|
||||
"multi"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -5,12 +5,11 @@ using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.V2ray;
|
||||
using Netch.Servers.V2ray.Models;
|
||||
using Netch.Servers.VMess.Form;
|
||||
using Netch.Servers.Form;
|
||||
using Netch.Servers.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Servers.VMess
|
||||
namespace Netch.Servers
|
||||
{
|
||||
public class VMessUtil : IServerUtil
|
||||
{
|
||||
@@ -43,19 +42,21 @@ namespace Netch.Servers.VMess
|
||||
var server = (VMess)s;
|
||||
|
||||
var vmessJson = JsonSerializer.Serialize(new V2rayNSharing
|
||||
{
|
||||
v = 2,
|
||||
ps = server.Remark,
|
||||
add = server.Hostname,
|
||||
port = server.Port,
|
||||
id = server.UserID,
|
||||
aid = server.AlterID,
|
||||
net = server.TransferProtocol,
|
||||
type = server.FakeType,
|
||||
host = server.Host,
|
||||
path = server.Path,
|
||||
tls = server.TLSSecureType
|
||||
},
|
||||
{
|
||||
v = 2,
|
||||
ps = server.Remark,
|
||||
add = server.Hostname,
|
||||
port = server.Port,
|
||||
scy = server.EncryptMethod,
|
||||
id = server.UserID,
|
||||
aid = server.AlterID,
|
||||
net = server.TransferProtocol,
|
||||
type = server.FakeType,
|
||||
host = server.Host ?? "",
|
||||
path = server.Path ?? "",
|
||||
tls = server.TLSSecureType,
|
||||
sni = server.ServerName ?? ""
|
||||
},
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
@@ -91,19 +92,18 @@ namespace Netch.Servers.VMess
|
||||
|
||||
data.Remark = vmess.ps;
|
||||
data.Hostname = vmess.add;
|
||||
data.EncryptMethod = vmess.scy;
|
||||
data.Port = vmess.port;
|
||||
data.UserID = vmess.id;
|
||||
data.AlterID = vmess.aid;
|
||||
data.TransferProtocol = vmess.net;
|
||||
data.FakeType = vmess.type;
|
||||
data.ServerName = vmess.sni;
|
||||
|
||||
if (data.TransferProtocol == "quic")
|
||||
{
|
||||
if (VMessGlobal.QUIC.Contains(vmess.host!))
|
||||
{
|
||||
data.QUICSecure = vmess.host;
|
||||
data.QUICSecret = vmess.path;
|
||||
}
|
||||
data.QUICSecure = vmess.host;
|
||||
data.QUICSecret = vmess.path;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -112,7 +112,6 @@ namespace Netch.Servers.VMess
|
||||
}
|
||||
|
||||
data.TLSSecureType = vmess.tls;
|
||||
data.EncryptMethod = "auto"; // V2Ray 加密方式不包括在链接中,主动添加一个
|
||||
|
||||
return new[] { data };
|
||||
}
|
||||
|
||||
148
Netch/Services/Updater.cs
Normal file
148
Netch/Services/Updater.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Netch.Models;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Services
|
||||
{
|
||||
public class Updater
|
||||
{
|
||||
#region Static
|
||||
|
||||
#endregion
|
||||
|
||||
#region Class
|
||||
|
||||
private string UpdateFile { get; }
|
||||
|
||||
private string InstallDirectory { get; }
|
||||
|
||||
private readonly string _tempDirectory;
|
||||
private static readonly string[] KeepDirectories = { "data", "mode\\Custom", "logging" };
|
||||
private static readonly string[] KeepFiles = { ModeHelper.DisableModeDirectoryFileName };
|
||||
|
||||
internal Updater(string updateFile, string installDirectory)
|
||||
{
|
||||
UpdateFile = updateFile;
|
||||
InstallDirectory = installDirectory;
|
||||
_tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
|
||||
Directory.CreateDirectory(_tempDirectory);
|
||||
}
|
||||
|
||||
#region Apply Update
|
||||
|
||||
internal void ApplyUpdate()
|
||||
{
|
||||
var extractPath = Path.Combine(_tempDirectory, "extract");
|
||||
|
||||
int exitCode;
|
||||
if ((exitCode = Extract(extractPath)) != 0)
|
||||
throw new MessageException(i18N.Translate($"7za unexpectedly exited. ({exitCode})"));
|
||||
|
||||
// update archive file must have a top-level directory "Netch"
|
||||
var updateDirectory = Path.Combine(extractPath, "Netch");
|
||||
if (!Directory.Exists(updateDirectory))
|
||||
throw new MessageException(i18N.Translate("Update file top-level directory not exist"));
|
||||
|
||||
// {_tempDirectory}/extract/Netch/Netch.exe
|
||||
var updateMainProgramFilePath = Path.Combine(updateDirectory, "Netch.exe");
|
||||
if (!File.Exists(updateMainProgramFilePath))
|
||||
throw new MessageException(i18N.Translate($"Update file main program not exist"));
|
||||
|
||||
MarkFilesOld();
|
||||
|
||||
// move {tempDirectory}\extract\Netch to install folder
|
||||
MoveFilesOver(updateDirectory, InstallDirectory);
|
||||
}
|
||||
|
||||
private void MarkFilesOld()
|
||||
{
|
||||
var keepDirFullPath = KeepDirectories.Select(d => Path.Combine(InstallDirectory, d)).ToImmutableList();
|
||||
|
||||
foreach (var file in Directory.GetFiles(InstallDirectory, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (keepDirFullPath.Any(p => file.StartsWith(p)))
|
||||
continue;
|
||||
|
||||
if (KeepFiles.Contains(Path.GetFileName(file)))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
File.Move(file, file + ".old");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "failed to rename file \"{File}\"", file);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int Extract(string destDirName)
|
||||
{
|
||||
// release 7za.exe to {tempDirectory}\7za.exe
|
||||
var temp7za = Path.Combine(_tempDirectory, "7za.exe");
|
||||
|
||||
if (!File.Exists(temp7za))
|
||||
File.WriteAllBytes(temp7za, Resources._7za);
|
||||
|
||||
var argument = new StringBuilder($" x \"{UpdateFile}\" -o\"{destDirName}\" -y");
|
||||
var process = Process.Start(new ProcessStartInfo(temp7za, argument.ToString())
|
||||
{
|
||||
UseShellExecute = false
|
||||
})!;
|
||||
|
||||
process.WaitForExit();
|
||||
return process.ExitCode;
|
||||
}
|
||||
|
||||
private static void MoveFilesOver(string source, string target)
|
||||
{
|
||||
foreach (string directory in Directory.GetDirectories(source))
|
||||
{
|
||||
string dirName = Path.GetFileName(directory);
|
||||
|
||||
if (!Directory.Exists(Path.Combine(target, dirName)))
|
||||
Directory.CreateDirectory(Path.Combine(target, dirName));
|
||||
|
||||
MoveFilesOver(directory, Path.Combine(target, dirName));
|
||||
}
|
||||
|
||||
foreach (string file in Directory.GetFiles(source))
|
||||
{
|
||||
var destFile = Path.Combine(target, Path.GetFileName(file));
|
||||
File.Delete(destFile);
|
||||
File.Move(file, destFile);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Clean files marked as old when start
|
||||
|
||||
public static void CleanOld(string targetPath)
|
||||
{
|
||||
foreach (var f in Directory.GetFiles(targetPath, "*.old", SearchOption.AllDirectories))
|
||||
try
|
||||
{
|
||||
File.Delete(f);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Properties;
|
||||
using Netch.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace Netch.Updater
|
||||
{
|
||||
public class Updater
|
||||
{
|
||||
#region Download Update and apply update
|
||||
|
||||
/// <summary>
|
||||
/// Download Update and apply update (all arguments are FullPath)
|
||||
/// </summary>
|
||||
/// <param name="downloadDirectory"></param>
|
||||
/// <param name="installDirectory"></param>
|
||||
/// <param name="onDownloadProgressChanged"></param>
|
||||
/// <param name="keyword"></param>
|
||||
/// <exception cref="MessageException"></exception>
|
||||
public static void DownloadAndUpdate(string downloadDirectory,
|
||||
string installDirectory,
|
||||
DownloadProgressChangedEventHandler onDownloadProgressChanged,
|
||||
string? keyword = null)
|
||||
{
|
||||
UpdateChecker.GetLatestUpdateFileNameAndHash(out var updateFileName, out var sha256, keyword);
|
||||
|
||||
// update file Full Path
|
||||
var updateFile = Path.Combine(downloadDirectory, updateFileName);
|
||||
var updater = new Updater(updateFile, installDirectory);
|
||||
|
||||
if (File.Exists(updateFile))
|
||||
{
|
||||
if (Utils.Utils.SHA256CheckSum(updateFile) == sha256)
|
||||
{
|
||||
updater.ApplyUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
File.Delete(updateFile);
|
||||
}
|
||||
|
||||
DownloadUpdateFile(onDownloadProgressChanged, updateFile, sha256);
|
||||
updater.ApplyUpdate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download Update File
|
||||
/// </summary>
|
||||
/// <param name="onDownloadProgressChanged"></param>
|
||||
/// <param name="fileFullPath"></param>
|
||||
/// <param name="sha256"></param>
|
||||
/// <exception cref="MessageException"></exception>
|
||||
private static void DownloadUpdateFile(DownloadProgressChangedEventHandler onDownloadProgressChanged, string fileFullPath, string sha256)
|
||||
{
|
||||
using WebClient client = new();
|
||||
try
|
||||
{
|
||||
client.DownloadProgressChanged += onDownloadProgressChanged;
|
||||
client.DownloadFile(new Uri(UpdateChecker.LatestRelease.assets[0].browser_download_url), fileFullPath);
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.DownloadProgressChanged -= onDownloadProgressChanged;
|
||||
}
|
||||
|
||||
if (Utils.Utils.SHA256CheckSum(fileFullPath) != sha256)
|
||||
throw new MessageException(i18N.Translate("The downloaded file has the wrong hash"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly string _updateFile;
|
||||
private readonly string _installDirectory;
|
||||
private readonly string _tempDirectory;
|
||||
|
||||
private Updater(string updateFile, string installDirectory)
|
||||
{
|
||||
_updateFile = updateFile;
|
||||
_installDirectory = installDirectory;
|
||||
_tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
|
||||
Directory.CreateDirectory(_tempDirectory);
|
||||
}
|
||||
|
||||
#region Apply Update
|
||||
|
||||
private static readonly ImmutableArray<string> KeepDirectories = new List<string> { "data", "mode\\Custom", "logging" }.ToImmutableArray();
|
||||
|
||||
private void ApplyUpdate()
|
||||
{
|
||||
var mainForm = Global.MainForm;
|
||||
|
||||
#region PreUpdate
|
||||
|
||||
ModeHelper.SuspendWatcher = true;
|
||||
// Stop and Save
|
||||
mainForm.Stop();
|
||||
Configuration.Save();
|
||||
|
||||
// Backup Configuration file
|
||||
try
|
||||
{
|
||||
File.Copy(Configuration.SettingFileFullName, Configuration.SettingFileFullName + ".bak", true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// extract Update file to {tempDirectory}\extract
|
||||
var extractPath = Path.Combine(_tempDirectory, "extract");
|
||||
int exitCode;
|
||||
if ((exitCode = Extract(extractPath, true)) != 0)
|
||||
throw new Exception(i18N.Translate($"7za exit with code {exitCode}"));
|
||||
|
||||
// rename install directory files with .old suffix unless in keep folders
|
||||
MarkFilesOld();
|
||||
|
||||
// move {tempDirectory}\extract\Netch to install folder
|
||||
MoveAllFilesOver(Path.Combine(extractPath, "Netch"), _installDirectory);
|
||||
|
||||
// release mutex, exit
|
||||
mainForm.Invoke(new Action(Netch.SingleInstance.Dispose));
|
||||
Process.Start(Global.NetchExecutable);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private void MarkFilesOld()
|
||||
{
|
||||
// extend keepDirectories relative path to absolute path
|
||||
var extendedKeepDirectories = KeepDirectories.Select(d => Path.Combine(_installDirectory, d)).ToImmutableArray();
|
||||
|
||||
// weed out keep files
|
||||
List<string> filesToDelete = new();
|
||||
foreach (var file in Directory.GetFiles(_installDirectory, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (extendedKeepDirectories.Any(p => file.StartsWith(p)))
|
||||
continue;
|
||||
|
||||
if (Path.GetFileName(file) is ModeHelper.DisableModeDirectoryFileName)
|
||||
continue;
|
||||
|
||||
filesToDelete.Add(file);
|
||||
}
|
||||
|
||||
// rename files
|
||||
foreach (var file in filesToDelete)
|
||||
try
|
||||
{
|
||||
File.Move(file, file + ".old");
|
||||
}
|
||||
catch
|
||||
{
|
||||
Global.Logger.Error($"failed to rename file \"{file}\"");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private int Extract(string destDirName, bool overwrite)
|
||||
{
|
||||
// release 7za.exe to {tempDirectory}\7za.exe
|
||||
var temp7za = Path.Combine(_tempDirectory, "7za.exe");
|
||||
|
||||
if (!File.Exists(temp7za))
|
||||
File.WriteAllBytes(temp7za, Resources._7za);
|
||||
|
||||
// run 7za
|
||||
var argument = new StringBuilder();
|
||||
argument.Append($" x \"{_updateFile}\" -o\"{destDirName}\"");
|
||||
if (overwrite)
|
||||
argument.Append(" -y");
|
||||
|
||||
var process = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
UseShellExecute = false,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
FileName = temp7za,
|
||||
Arguments = argument.ToString()
|
||||
})!;
|
||||
|
||||
process.WaitForExit();
|
||||
return process.ExitCode;
|
||||
}
|
||||
|
||||
private static void MoveAllFilesOver(string source, string target)
|
||||
{
|
||||
foreach (string directory in Directory.GetDirectories(source))
|
||||
{
|
||||
string dirName = Path.GetFileName(directory);
|
||||
|
||||
if (!Directory.Exists(Path.Combine(target, dirName)))
|
||||
Directory.CreateDirectory(Path.Combine(target, dirName));
|
||||
|
||||
MoveAllFilesOver(directory, Path.Combine(target, dirName));
|
||||
}
|
||||
|
||||
foreach (string file in Directory.GetFiles(source))
|
||||
{
|
||||
var destFile = Path.Combine(target, Path.GetFileName(file));
|
||||
File.Delete(destFile);
|
||||
File.Move(file, destFile);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Clean files marked as old when start
|
||||
|
||||
public static void CleanOld(string targetPath)
|
||||
{
|
||||
foreach (var f in Directory.GetFiles(targetPath, "*.old", SearchOption.AllDirectories))
|
||||
try
|
||||
{
|
||||
File.Delete(f);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
using Microsoft.Diagnostics.Tracing.Parsers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Diagnostics.Tracing.Parsers;
|
||||
using Microsoft.Diagnostics.Tracing.Session;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -56,9 +58,8 @@ namespace Netch.Utils
|
||||
{
|
||||
case null:
|
||||
break;
|
||||
case Guard instanceController:
|
||||
if (instanceController.Instance != null)
|
||||
instances.Add(instanceController.Instance);
|
||||
case Guard guard:
|
||||
instances.Add(guard.Instance);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -68,17 +69,17 @@ namespace Netch.Utils
|
||||
{
|
||||
case null:
|
||||
break;
|
||||
case NFController _:
|
||||
case NFController:
|
||||
instances.Add(Process.GetCurrentProcess());
|
||||
break;
|
||||
case Guard instanceController:
|
||||
instances.Add(instanceController.Instance!);
|
||||
case Guard guard:
|
||||
instances.Add(guard.Instance);
|
||||
break;
|
||||
}
|
||||
|
||||
var processList = instances.Select(instance => instance.Id).ToList();
|
||||
var processList = instances.Select(instance => instance.Id).ToHashSet();
|
||||
|
||||
Global.Logger.Info("流量统计进程:" + string.Join(",", instances.Select(instance => $"({instance.Id})" + instance.ProcessName).ToArray()));
|
||||
Log.Information("流量统计进程: {Processes}", string.Join(',', instances.Select(v => $"({v.Id}){v.ProcessName}")));
|
||||
|
||||
received = 0;
|
||||
|
||||
@@ -117,7 +118,7 @@ namespace Netch.Utils
|
||||
|
||||
while (Global.MainForm.State != State.Stopped)
|
||||
{
|
||||
Task.Delay(1000).Wait();
|
||||
Thread.Sleep(1000);
|
||||
lock (counterLock)
|
||||
Global.MainForm.OnBandwidthUpdated(received);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using Netch.Models;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Models;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -14,9 +16,15 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
public static string DataDirectoryFullName => Path.Combine(Global.NetchDir, "data");
|
||||
|
||||
public static string SettingFileFullName => $"{DataDirectoryFullName}\\settings.json";
|
||||
public static string FileFullName => Path.Combine(DataDirectoryFullName, FileName);
|
||||
|
||||
private static readonly JsonSerializerOptions JsonSerializerOptions = Global.NewDefaultJsonSerializerOptions;
|
||||
private static string BackupFileFullName => Path.Combine(DataDirectoryFullName, BackupFileName);
|
||||
|
||||
private const string FileName = "settings.json";
|
||||
|
||||
private const string BackupFileName = "settings.json.bak";
|
||||
|
||||
private static readonly JsonSerializerOptions JsonSerializerOptions = Global.NewCustomJsonSerializerOptions();
|
||||
|
||||
static Configuration()
|
||||
{
|
||||
@@ -24,34 +32,47 @@ namespace Netch.Utils
|
||||
JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置
|
||||
/// </summary>
|
||||
public static void Load()
|
||||
public static async Task LoadAsync()
|
||||
{
|
||||
if (File.Exists(SettingFileFullName))
|
||||
try
|
||||
{
|
||||
try
|
||||
if (!File.Exists(FileFullName))
|
||||
{
|
||||
using var fileStream = File.OpenRead(SettingFileFullName);
|
||||
var settings = JsonSerializer.DeserializeAsync<Setting>(fileStream, JsonSerializerOptions).Result!;
|
||||
await SaveAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
if (await LoadAsyncCore(FileFullName))
|
||||
return;
|
||||
|
||||
Log.Information("尝试加载备份配置文件 {FileName}", BackupFileFullName);
|
||||
await LoadAsyncCore(BackupFileFullName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "加载配置异常");
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
private static async ValueTask<bool> LoadAsyncCore(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
|
||||
await using (fs.ConfigureAwait(false))
|
||||
{
|
||||
var settings = (await JsonSerializer.DeserializeAsync<Setting>(fs, JsonSerializerOptions).ConfigureAwait(false))!;
|
||||
|
||||
CheckSetting(settings);
|
||||
|
||||
Global.Settings = settings;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Error(e.ToString());
|
||||
Global.Logger.ShowLog();
|
||||
Environment.Exit(-1);
|
||||
Global.Settings = null!;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception e)
|
||||
{
|
||||
// 保存默认设置
|
||||
Save();
|
||||
Log.Error(e, @"从 {FileName} 加载配置异常", filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,13 +91,26 @@ namespace Netch.Utils
|
||||
/// <summary>
|
||||
/// 保存配置
|
||||
/// </summary>
|
||||
public static void Save()
|
||||
public static async Task SaveAsync()
|
||||
{
|
||||
if (!Directory.Exists(DataDirectoryFullName))
|
||||
Directory.CreateDirectory(DataDirectoryFullName);
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(DataDirectoryFullName))
|
||||
Directory.CreateDirectory(DataDirectoryFullName);
|
||||
|
||||
using var fileStream = File.Create(SettingFileFullName);
|
||||
JsonSerializer.SerializeAsync(fileStream, Global.Settings, JsonSerializerOptions).Wait();
|
||||
var tempFile = Path.Combine(DataDirectoryFullName, FileFullName + ".tmp");
|
||||
var fileStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true);
|
||||
await using (fileStream.ConfigureAwait(false))
|
||||
{
|
||||
await JsonSerializer.SerializeAsync(fileStream, Global.Settings, JsonSerializerOptions).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
File.Replace(tempFile, FileFullName, BackupFileFullName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "保存配置异常");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,12 +13,7 @@ namespace Netch.Utils
|
||||
/// </summary>
|
||||
private static readonly Hashtable Cache = new();
|
||||
|
||||
/// <summary>
|
||||
/// 查询
|
||||
/// </summary>
|
||||
/// <param name="hostname">主机名</param>
|
||||
/// <returns></returns>
|
||||
public static IPAddress? Lookup(string hostname)
|
||||
public static IPAddress? Lookup(string hostname, int timeout = 3000)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -26,7 +21,7 @@ namespace Netch.Utils
|
||||
return Cache[hostname] as IPAddress;
|
||||
|
||||
var task = Dns.GetHostAddressesAsync(hostname);
|
||||
if (!task.Wait(1000))
|
||||
if (!task.Wait(timeout))
|
||||
return null;
|
||||
|
||||
if (task.Result.Length == 0)
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using WindowsFirewallHelper;
|
||||
using WindowsFirewallHelper.FirewallRules;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -17,7 +18,7 @@ namespace Netch.Utils
|
||||
{
|
||||
if (!FirewallWAS.IsSupported)
|
||||
{
|
||||
Global.Logger.Warning("不支持防火墙");
|
||||
Log.Warning("不支持防火墙");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,7 +38,7 @@ namespace Netch.Utils
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Warning("添加防火墙规则错误(如已关闭防火墙则可无视此错误)\n" + e);
|
||||
Log.Warning(e, "添加防火墙规则错误");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ namespace Netch.Utils
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Warning("清除防火墙规则错误\n" + e);
|
||||
Log.Warning(e, "清除防火墙规则错误");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using Netch.Controllers;
|
||||
using Netch.Enums;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Netch.Servers;
|
||||
using Netch.Servers.Shadowsocks;
|
||||
using Netch.Servers.Socks5;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -14,32 +16,47 @@ namespace Netch.Utils
|
||||
{
|
||||
public const string DisableModeDirectoryFileName = "disabled";
|
||||
|
||||
private static FileSystemWatcher _fileSystemWatcher = null!;
|
||||
|
||||
public static string ModeDirectoryFullName => Path.Combine(Global.NetchDir, "mode");
|
||||
|
||||
private static readonly FileSystemWatcher FileSystemWatcher;
|
||||
|
||||
public static bool SuspendWatcher { get; set; } = false;
|
||||
|
||||
static ModeHelper()
|
||||
public static bool SuspendWatcher
|
||||
{
|
||||
FileSystemWatcher = new FileSystemWatcher(ModeDirectoryFullName)
|
||||
get => _fileSystemWatcher.EnableRaisingEvents;
|
||||
set => _fileSystemWatcher.EnableRaisingEvents = value;
|
||||
}
|
||||
|
||||
public static void InitWatcher()
|
||||
{
|
||||
_fileSystemWatcher = new FileSystemWatcher(ModeDirectoryFullName)
|
||||
{
|
||||
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.FileName,
|
||||
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName,
|
||||
IncludeSubdirectories = true,
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
|
||||
FileSystemWatcher.Changed += OnModeChanged;
|
||||
FileSystemWatcher.Created += OnModeChanged;
|
||||
FileSystemWatcher.Deleted += OnModeChanged;
|
||||
FileSystemWatcher.Renamed += OnModeChanged;
|
||||
var created = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(h => _fileSystemWatcher.Created += h,
|
||||
h => _fileSystemWatcher.Created -= h)
|
||||
.Select(x => x.EventArgs);
|
||||
|
||||
var changed = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(h => _fileSystemWatcher.Changed += h,
|
||||
h => _fileSystemWatcher.Changed -= h)
|
||||
.Select(x => x.EventArgs);
|
||||
|
||||
var deleted = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(h => _fileSystemWatcher.Deleted += h,
|
||||
h => _fileSystemWatcher.Deleted -= h)
|
||||
.Select(x => x.EventArgs);
|
||||
|
||||
var renamed = Observable.FromEventPattern<RenamedEventHandler, RenamedEventArgs>(h => _fileSystemWatcher.Renamed += h,
|
||||
h => _fileSystemWatcher.Renamed -= h)
|
||||
.Select(x => x.EventArgs);
|
||||
|
||||
var o = Observable.Merge(created, deleted, renamed, changed);
|
||||
o.Throttle(TimeSpan.FromSeconds(3)).Subscribe(_ => OnModeChange(), exception => Log.Error(exception, "FileSystemWatcherError"));
|
||||
}
|
||||
|
||||
private static void OnModeChanged(object sender, FileSystemEventArgs e)
|
||||
private static void OnModeChange()
|
||||
{
|
||||
if (SuspendWatcher)
|
||||
return;
|
||||
|
||||
Load();
|
||||
Global.MainForm.LoadModes();
|
||||
}
|
||||
@@ -58,23 +75,19 @@ namespace Netch.Utils
|
||||
return Path.Combine(ModeDirectoryFullName, relativeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从模式文件夹读取模式
|
||||
/// </summary>
|
||||
public static void Load()
|
||||
{
|
||||
Global.Modes.Clear();
|
||||
LoadModeDirectory(ModeDirectoryFullName);
|
||||
|
||||
LoadCore(ModeDirectoryFullName);
|
||||
Sort();
|
||||
}
|
||||
|
||||
private static void LoadModeDirectory(string modeDirectory)
|
||||
private static void LoadCore(string modeDirectory)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var directory in Directory.GetDirectories(modeDirectory))
|
||||
LoadModeDirectory(directory);
|
||||
LoadCore(directory);
|
||||
|
||||
// skip Directory with a disabled file in
|
||||
if (File.Exists(Path.Combine(modeDirectory, DisableModeDirectoryFileName)))
|
||||
@@ -87,7 +100,7 @@ namespace Netch.Utils
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Warning($"Load mode \"{file}\" failed: {e.Message}");
|
||||
Log.Warning(e, "Load mode \"{FileName}\" failed", file);
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -106,7 +119,11 @@ namespace Netch.Utils
|
||||
if (mode.FullName == null)
|
||||
throw new ArgumentException(nameof(mode.FullName));
|
||||
|
||||
File.Delete(mode.FullName);
|
||||
Global.MainForm.ModeComboBox.Items.Remove(mode);
|
||||
Global.Modes.Remove(mode);
|
||||
|
||||
if (File.Exists(mode.FullName))
|
||||
File.Delete(mode.FullName);
|
||||
}
|
||||
|
||||
public static bool SkipServerController(Server server, Mode mode)
|
||||
@@ -142,7 +159,7 @@ namespace Netch.Utils
|
||||
case ModeType.Pcap2Socks:
|
||||
return new PcapController();
|
||||
default:
|
||||
Global.Logger.Error("未知模式类型");
|
||||
Log.Error("未知模式类型");
|
||||
throw new MessageException("未知模式类型");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Netch.Models;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
@@ -47,7 +49,11 @@ namespace Netch.Utils
|
||||
if (metric != null)
|
||||
arguments += $"metric={metric} ";
|
||||
|
||||
Utils.ProcessRunHiddenAsync("netsh", arguments).Wait();
|
||||
Process.Start(new ProcessStartInfo("netsh.exe", arguments)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
Verb = "runas"
|
||||
})!.WaitForExit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using Netch.Models;
|
||||
using Serilog;
|
||||
using static Vanara.PInvoke.IpHlpApi;
|
||||
using static Vanara.PInvoke.Ws2_32;
|
||||
|
||||
@@ -24,7 +25,7 @@ namespace Netch.Utils
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Error("获取保留端口失败: " + e);
|
||||
Log.Error(e, "获取保留端口错误");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,11 +163,12 @@ namespace Netch.Utils
|
||||
/// <summary>
|
||||
/// 检查端口类型
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum PortType
|
||||
{
|
||||
TCP,
|
||||
UDP,
|
||||
Both
|
||||
TCP = 0x01,
|
||||
UDP = 0x10,
|
||||
Both = TCP | UDP
|
||||
}
|
||||
|
||||
public class PortInUseException : Exception
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Sockets;
|
||||
using Netch.Interops;
|
||||
using Netch.Models;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -18,7 +19,7 @@ namespace Netch.Utils
|
||||
{
|
||||
if (!TryParseIPNetwork(rule, out var network, out var cidr))
|
||||
{
|
||||
Global.Logger.Warning($"invalid rule {rule}");
|
||||
Log.Warning("invalid rule {Rule}", rule);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -40,7 +41,7 @@ namespace Netch.Utils
|
||||
{
|
||||
if (!TryParseIPNetwork(rule, out var network, out var cidr))
|
||||
{
|
||||
Global.Logger.Warning($"invalid rule {rule}");
|
||||
Log.Warning("invalid rule {Rule}",rule);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using Netch.Interfaces;
|
||||
using Netch.Models;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -30,7 +31,7 @@ namespace Netch.Utils
|
||||
public static class DelayTestHelper
|
||||
{
|
||||
private static readonly Timer Timer;
|
||||
private static bool _mux;
|
||||
private static readonly object TestAllLock = new();
|
||||
|
||||
public static readonly NumberRange Range = new(0, int.MaxValue / 1000);
|
||||
|
||||
@@ -68,20 +69,22 @@ namespace Netch.Utils
|
||||
|
||||
public static void TestAllDelay()
|
||||
{
|
||||
if (_mux)
|
||||
if (!Monitor.TryEnter(TestAllLock))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_mux = true;
|
||||
Parallel.ForEach(Global.Settings.Server, new ParallelOptions { MaxDegreeOfParallelism = 16 }, server => { server.Test(); });
|
||||
_mux = false;
|
||||
TestDelayFinished?.Invoke(null, new EventArgs());
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(TestAllLock);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateInterval()
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Shadowsocks;
|
||||
using Netch.Servers.Shadowsocks.Models;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Shadowsocks;
|
||||
using Netch.Servers.Shadowsocks.Models;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -45,7 +46,6 @@ namespace Netch.Utils
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
var errorFlag = false;
|
||||
foreach (var line in text.GetLines())
|
||||
{
|
||||
try
|
||||
@@ -54,17 +54,13 @@ namespace Netch.Utils
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
errorFlag = true;
|
||||
Global.Logger.Error(e.ToString());
|
||||
Log.Error(e, "从分享链接导入服务器异常");
|
||||
}
|
||||
}
|
||||
|
||||
if (errorFlag)
|
||||
Global.Logger.ShowLog();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.Logger.Error(e.ToString());
|
||||
Log.Error(e, "从分享链接导入服务器异常");
|
||||
}
|
||||
|
||||
return list;
|
||||
@@ -89,7 +85,7 @@ namespace Netch.Utils
|
||||
if (util != null)
|
||||
list.AddRange(util.ParseUri(text));
|
||||
else
|
||||
Global.Logger.Warning($"无法处理 {scheme} 协议订阅链接");
|
||||
Log.Warning("无法处理 {Scheme} 协议订阅链接", scheme);
|
||||
}
|
||||
|
||||
foreach (var node in list.Where(node => !node.Remark.IsNullOrWhiteSpace()))
|
||||
@@ -128,7 +124,7 @@ namespace Netch.Utils
|
||||
|
||||
public static string GetNetchLink(Server s)
|
||||
{
|
||||
var jsonSerializerOptions = Global.NewDefaultJsonSerializerOptions;
|
||||
var jsonSerializerOptions = Global.NewCustomJsonSerializerOptions();
|
||||
jsonSerializerOptions.WriteIndented = false;
|
||||
return "Netch://" + URLSafeBase64Encode(JsonSerializer.Serialize(s, jsonSerializerOptions));
|
||||
}
|
||||
|
||||
@@ -72,5 +72,15 @@ namespace Netch.Utils
|
||||
{
|
||||
return value.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public static string? ValueOrDefault(this string? value)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(value) ? null : value;
|
||||
}
|
||||
|
||||
public static string[]? SplitOrDefault(this string? value)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(value) ? value.Split(',') : default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -53,7 +54,7 @@ namespace Netch.Utils
|
||||
catch (Exception e)
|
||||
{
|
||||
Global.MainForm.NotifyTip($"{i18N.TranslateFormat("Update servers error from {0}", item.Remark)}\n{e.Message}", info: false);
|
||||
Global.Logger.Error(e.ToString());
|
||||
Log.Warning(e, "更新服务器失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
60
Netch/Utils/SystemInfo.cs
Normal file
60
Netch/Utils/SystemInfo.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public static class SystemInfo
|
||||
{
|
||||
public static IEnumerable<string> SystemDrivers(bool allDriver)
|
||||
{
|
||||
var mc = new ManagementClass("Win32_SystemDriver");
|
||||
foreach (var obj in mc.GetInstances().Cast<ManagementObject>())
|
||||
{
|
||||
if (!(bool) obj["Started"])
|
||||
continue;
|
||||
|
||||
var path = obj["PathName"].ToString();
|
||||
if (path == null)
|
||||
continue;
|
||||
|
||||
var vendorExclude = new[] {"microsoft", "intel", "amd", "nvidia", "realtek"};
|
||||
var vendorName = FileVersionInfo.GetVersionInfo(path).LegalCopyright ?? string.Empty;
|
||||
if (!allDriver && vendorExclude.Any(s => vendorName.Contains(s, StringComparison.OrdinalIgnoreCase)))
|
||||
continue;
|
||||
|
||||
yield return $"{obj["Caption"]} [{obj["Description"]}]({vendorName})\n\t{obj["PathName"]}";
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<string> Processes(bool mask)
|
||||
{
|
||||
var hashset = new HashSet<string>();
|
||||
foreach (var process in Process.GetProcesses())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (process.Id is 0 or 4)
|
||||
continue;
|
||||
|
||||
if (process.MainModule!.FileName!.StartsWith(Environment.GetFolderPath(Environment.SpecialFolder.Windows), StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
var path = process.MainModule.FileName;
|
||||
|
||||
if (mask)
|
||||
path = path.Replace(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "%USERPROFILE%");
|
||||
|
||||
hashset.Add(path);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
return hashset;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Netch/Utils/TplExtensions.cs
Normal file
11
Netch/Utils/TplExtensions.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public static class TplExtensions
|
||||
{
|
||||
public static void Forget(this Task? task)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,13 +13,14 @@ using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using MaxMind.GeoIP2;
|
||||
using Microsoft.Win32.TaskScheduler;
|
||||
using Serilog;
|
||||
using Task = System.Threading.Tasks.Task;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public static class Utils
|
||||
{
|
||||
public static bool Open(string path)
|
||||
public static void Open(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -29,12 +30,10 @@ namespace Netch.Utils
|
||||
Arguments = path,
|
||||
UseShellExecute = true
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
Log.Warning(e, "打开 {Uri} 失败", path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,16 +103,22 @@ namespace Netch.Utils
|
||||
{
|
||||
try
|
||||
{
|
||||
var sha256 = SHA256.Create();
|
||||
using var fileStream = File.OpenRead(filePath);
|
||||
return string.Concat(sha256.ComputeHash(fileStream).Select(b => b.ToString("x2")));
|
||||
return SHA256ComputeCore(fileStream);
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warning(e, $"Compute file \"{filePath}\" sha256 failed");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static string SHA256ComputeCore(Stream stream)
|
||||
{
|
||||
using var sha256 = SHA256.Create();
|
||||
return string.Concat(sha256.ComputeHash(stream).Select(b => b.ToString("x2")));
|
||||
}
|
||||
|
||||
public static string GetFileVersion(string file)
|
||||
{
|
||||
if (File.Exists(file))
|
||||
@@ -201,6 +206,9 @@ namespace Netch.Utils
|
||||
|
||||
td.Settings.ExecutionTimeLimit = TimeSpan.Zero;
|
||||
td.Settings.DisallowStartIfOnBatteries = false;
|
||||
td.Settings.StopIfGoingOnBatteries = false;
|
||||
td.Settings.IdleSettings.StopOnIdleEnd = false;
|
||||
td.Settings.IdleSettings.RestartOnIdle = false;
|
||||
td.Settings.RunOnlyIfIdle = false;
|
||||
td.Settings.Compatibility = TaskCompatibility.V2_1;
|
||||
|
||||
@@ -226,37 +234,6 @@ namespace Netch.Utils
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task ProcessRunHiddenAsync(string fileName, string arguments = "", bool print = true)
|
||||
{
|
||||
var p = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = fileName,
|
||||
Arguments = arguments,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
Verb = "runas",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
|
||||
Global.Logger.Debug($"{fileName} {arguments}");
|
||||
|
||||
p.Start();
|
||||
var output = await p.StandardOutput.ReadToEndAsync();
|
||||
var error = await p.StandardError.ReadToEndAsync();
|
||||
if (print)
|
||||
{
|
||||
Console.Write(output);
|
||||
Console.Write(error);
|
||||
}
|
||||
|
||||
await p.WaitForExitAsync();
|
||||
}
|
||||
|
||||
public static int SubnetToCidr(string value)
|
||||
{
|
||||
var subnet = IPAddress.Parse(value);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
@@ -8,7 +9,7 @@ namespace Netch.Utils
|
||||
public static class WebUtil
|
||||
{
|
||||
public const string DefaultUserAgent =
|
||||
@"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.55";
|
||||
@"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.67";
|
||||
|
||||
static WebUtil()
|
||||
{
|
||||
@@ -51,11 +52,12 @@ namespace Netch.Utils
|
||||
/// <param name="rep"></param>
|
||||
/// <param name="encoding">编码,默认UTF-8</param>
|
||||
/// <returns></returns>
|
||||
public static string DownloadString(HttpWebRequest req, out HttpWebResponse rep, string encoding = "UTF-8")
|
||||
public static string DownloadString(HttpWebRequest req, out HttpWebResponse rep, Encoding? encoding = null)
|
||||
{
|
||||
encoding ??= Encoding.UTF8;
|
||||
rep = (HttpWebResponse)req.GetResponse();
|
||||
using var responseStream = rep.GetResponseStream();
|
||||
using var streamReader = new StreamReader(responseStream, Encoding.GetEncoding(encoding));
|
||||
using var streamReader = new StreamReader(responseStream, encoding);
|
||||
|
||||
return streamReader.ReadToEnd();
|
||||
}
|
||||
@@ -66,29 +68,51 @@ namespace Netch.Utils
|
||||
/// <param name="req"></param>
|
||||
/// <param name="encoding">编码,默认UTF-8</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> DownloadStringAsync(HttpWebRequest req, string encoding = "UTF-8")
|
||||
public static async Task<string> DownloadStringAsync(HttpWebRequest req, Encoding? encoding = null)
|
||||
{
|
||||
encoding ??= Encoding.UTF8;
|
||||
using var webResponse = await req.GetResponseAsync();
|
||||
await using var responseStream = webResponse.GetResponseStream();
|
||||
using var streamReader = new StreamReader(responseStream, Encoding.GetEncoding(encoding));
|
||||
using var streamReader = new StreamReader(responseStream, encoding);
|
||||
|
||||
return await streamReader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步下载到文件
|
||||
/// </summary>
|
||||
/// <param name="req"></param>
|
||||
/// <param name="fileFullPath"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task DownloadFileAsync(HttpWebRequest req, string fileFullPath)
|
||||
public static async Task DownloadFileAsync(string address, string fileFullPath, IProgress<int>? progress = null)
|
||||
{
|
||||
using var webResponse = (HttpWebResponse)await req.GetResponseAsync();
|
||||
await using var input = webResponse.GetResponseStream();
|
||||
await using var fileStream = File.OpenWrite(fileFullPath);
|
||||
await DownloadFileAsync(CreateRequest(address), fileFullPath, progress);
|
||||
}
|
||||
|
||||
await input.CopyToAsync(fileStream);
|
||||
fileStream.Flush();
|
||||
public static async Task DownloadFileAsync(HttpWebRequest req, string fileFullPath, IProgress<int>? progress)
|
||||
{
|
||||
await using (var fileStream = File.Open(fileFullPath, FileMode.Create, FileAccess.Write))
|
||||
using (var webResponse = (HttpWebResponse)await req.GetResponseAsync())
|
||||
await using (var input = webResponse.GetResponseStream())
|
||||
using (var downloadTask = input.CopyToAsync(fileStream))
|
||||
{
|
||||
if (progress != null)
|
||||
ReportProgress(webResponse.ContentLength, downloadTask, fileStream, progress, 200).Forget();
|
||||
|
||||
await downloadTask;
|
||||
}
|
||||
|
||||
progress?.Report(100);
|
||||
}
|
||||
|
||||
private static async Task ReportProgress(long total, IAsyncResult downloadTask, Stream stream, IProgress<int> progress, int interval)
|
||||
{
|
||||
var n = 0;
|
||||
while (!downloadTask.IsCompleted)
|
||||
{
|
||||
var n1 = (int)((double)stream.Length / total * 100);
|
||||
if (n != n1)
|
||||
{
|
||||
n = n1;
|
||||
progress.Report(n);
|
||||
}
|
||||
|
||||
await Task.Delay(interval).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Windows.Forms;
|
||||
using Serilog;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
@@ -41,7 +42,7 @@ namespace Netch.Utils
|
||||
{
|
||||
var oldLangCode = LangCode;
|
||||
LangCode = languages.FirstOrDefault(s => GetLanguage(s).Equals(GetLanguage(LangCode))) ?? "en-US";
|
||||
Global.Logger.Info($"找不到语言 {oldLangCode}, 使用 {LangCode}");
|
||||
Log.Information("找不到语言 {OldLangCode}, 使用 {LangCode}",oldLangCode,LangCode);
|
||||
}
|
||||
|
||||
switch (LangCode)
|
||||
@@ -61,7 +62,7 @@ namespace Netch.Utils
|
||||
|
||||
if (!dictionary.Any())
|
||||
{
|
||||
Global.Logger.Error($"{LangCode} 语言文件错误");
|
||||
Log.Error("{LangCode} 语言文件错误", LangCode);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
22
README.md
22
README.md
@@ -1,18 +1,18 @@
|
||||
<p align="center"><img src="https://github.com/NetchX/Netch/blob/master/Netch/Resources/Netch.png?raw=true" width="128" /></p>
|
||||
<p align="center"><img src="https://cdn.jsdelivr.net/gh/netchx/netch/Netch/Resources/Netch.png" width="128" /></p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
# Netch
|
||||
A simple proxy client like SSTap
|
||||
A simple proxy client
|
||||
|
||||
[](https://t.me/netch_group)
|
||||
[](https://t.me/netch_channel)
|
||||
[](https://github.com/NetchX/Netch/releases)
|
||||
[](https://github.com/NetchX/Netch/releases)
|
||||
[](https://github.com/netchx/netch/releases)
|
||||
[](https://github.com/netchx/netch/releases)
|
||||
</div>
|
||||
|
||||
## Features
|
||||
Some features may not be implemented in version 1.0
|
||||
Some features may not be implemented in version 1
|
||||
|
||||
### Modes
|
||||
- ProcessMode - Use Netfilter driver to intercept process traffic
|
||||
@@ -27,15 +27,15 @@ Some features may not be implemented in version 1.0
|
||||
- [Shadowsocks](https://github.com/shadowsocks/shadowsocks-libev)
|
||||
- [ShadowsocksR](https://github.com/shadowsocksrr/shadowsocksr-libev)
|
||||
- [Trojan](https://trojan-gfw.github.io/trojan/)
|
||||
- [VMess](https://github.com/v2fly/v2ray-core)
|
||||
- [VLess](https://github.com/xtls/xray-core)
|
||||
- [VMess](https://github.com/v2fly/v2ray-core)
|
||||
|
||||
### Others
|
||||
- UDP NAT FullCone (May limited by your server)
|
||||
- .NET 5.0
|
||||
- .NET 5.0 x64
|
||||
|
||||
## Sponsor
|
||||
<a href="https://www.jetbrains.com/?from=Netch"><img src="jetbrains.svg" alt="JetBrains" width="200"/></a>
|
||||
<a href="https://www.jetbrains.com/?from=Netch"><img src="https://cdn.jsdelivr.net/gh/netchx/netch/jetbrains.svg" alt="JetBrains" width="200"/></a>
|
||||
|
||||
- [NeroCloud](https://nerocloud.io)
|
||||
|
||||
@@ -45,13 +45,11 @@ Some features may not be implemented in version 1.0
|
||||
|
||||
## Credit
|
||||
- [WinTUN](https://www.wintun.net)
|
||||
- [NetFilter](https://netfiltersdk.com)
|
||||
- [aioCloud](https://github.com/aiocloud)
|
||||
- [NetFilter](https://netfiltersdk.com)
|
||||
- [TAP-Windows](https://github.com/OpenVPN/tap-windows6)
|
||||
- [Shadowsocks](https://github.com/shadowsocks/shadowsocks-libev)
|
||||
- [ShadowsocksR](https://github.com/shadowsocksrr/shadowsocksr-libev)
|
||||
- [Trojan](https://github.com/trojan-gfw/trojan)
|
||||
- [V2Ray](https://github.com/v2fly/v2ray-core)
|
||||
- [XRay](https://github.com/xtls/xray-core)
|
||||
|
||||
## Stars
|
||||
[](https://starchart.cc/NetchX/Netch)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
param([string]$OutputPath)
|
||||
|
||||
$NetchDataURL="https://github.com/NetchX/NetchData/archive/refs/heads/master.zip"
|
||||
$NetchModeURL="https://github.com/NetchX/NetchMode/archive/refs/heads/master.zip"
|
||||
$NetchI18NURL="https://github.com/NetchX/NetchI18N/archive/refs/heads/master.zip"
|
||||
$NetchDataURL="https://github.com/netchx/netchdata/archive/refs/heads/master.zip"
|
||||
$NetchModeURL="https://github.com/netchx/netchmode/archive/refs/heads/master.zip"
|
||||
$NetchI18NURL="https://github.com/netchx/netchi18n/archive/refs/heads/master.zip"
|
||||
|
||||
$last=$(Get-Location)
|
||||
New-Item -ItemType Directory -Name $OutputPath | Out-Null
|
||||
@@ -20,17 +20,18 @@ New-Item -ItemType Directory -Name bin | Out-Null
|
||||
New-Item -ItemType Directory -Name mode | Out-Null
|
||||
New-Item -ItemType Directory -Name i18n | Out-Null
|
||||
|
||||
Copy-Item -Recurse -Force .\NetchData-master\* .\bin
|
||||
Copy-Item -Recurse -Force .\NetchMode-master\mode\* .\mode
|
||||
Copy-Item -Recurse -Force .\NetchI18N-master\i18n\* .\i18n
|
||||
Copy-Item -Recurse -Force .\netchdata-master\* .\bin
|
||||
Copy-Item -Recurse -Force .\netchmode-master\mode\* .\mode
|
||||
Copy-Item -Recurse -Force .\netchi18n-master\i18n\* .\i18n
|
||||
|
||||
Remove-Item -Recurse -Force NetchData-master
|
||||
Remove-Item -Recurse -Force NetchMode-master
|
||||
Remove-Item -Recurse -Force NetchI18N-master
|
||||
Remove-Item -Recurse -Force netchdata-master
|
||||
Remove-Item -Recurse -Force netchmode-master
|
||||
Remove-Item -Recurse -Force netchi18n-master
|
||||
Remove-Item -Force data.zip
|
||||
Remove-Item -Force mode.zip
|
||||
Remove-Item -Force i18n.zip
|
||||
|
||||
..\scripts\download\aiodns.ps1 -OutputPath bin
|
||||
..\scripts\download\cloak.ps1 -OutputPath bin
|
||||
..\scripts\download\xray-core.ps1 -OutputPath bin
|
||||
|
||||
|
||||
10
scripts/download/aiodns.ps1
Normal file
10
scripts/download/aiodns.ps1
Normal file
@@ -0,0 +1,10 @@
|
||||
param([string]$OutputPath)
|
||||
$address="https://github.com/aiocloud/aiodns/releases/download/1.0.4/aiodns.bin"
|
||||
$domains="https://raw.githubusercontent.com/aiocloud/aiodns/master/aiodns.conf"
|
||||
|
||||
Invoke-WebRequest -Uri $address -OutFile aiodns.bin
|
||||
Invoke-WebRequest -Uri $domains -OutFile aiodns.conf
|
||||
|
||||
Move-Item -Force aiodns.bin $OutputPath
|
||||
Move-Item -Force aiodns.conf $OutputPath
|
||||
exit 0
|
||||
@@ -1,5 +1,5 @@
|
||||
param([string]$OutputPath)
|
||||
$address="https://github.com/cbeuw/Cloak/releases/download/v2.5.3/ck-client-windows-amd64-v2.5.3.exe"
|
||||
$address="https://github.com/cbeuw/Cloak/releases/download/v2.5.4/ck-client-windows-amd64-v2.5.4.exe"
|
||||
|
||||
Invoke-WebRequest -Uri $address -OutFile ck-client.exe
|
||||
|
||||
|
||||
@@ -1,6 +1,2 @@
|
||||
param([string]$file)
|
||||
$hash = [Security.Cryptography.HashAlgorithm]::Create( "SHA256" )
|
||||
$path = (Resolve-Path -Path $file).Path
|
||||
$stream = ([IO.StreamReader]$path).BaseStream
|
||||
-join ($hash.ComputeHash($stream) | ForEach-Object { "{0:x2}" -f $_ })
|
||||
$stream.Close()
|
||||
(Get-FileHash $file -Algorithm SHA256).Hash.ToLower()
|
||||
|
||||
Reference in New Issue
Block a user