From 9b12ae946242f3e35eaf6e3eaa5df199182777a1 Mon Sep 17 00:00:00 2001
From: ChsBuffer <33744752+chsbuffer@users.noreply.github.com>
Date: Sat, 26 Sep 2020 15:00:18 +0800
Subject: [PATCH] Refactoring
---
.../EncryptedProxy/SSController.cs | 75 --
Netch/Controllers/ICSController.cs | 15 +-
Netch/Controllers/Interface/ModeController.cs | 6 +-
Netch/Controllers/MainController.cs | 45 +-
Netch/Controllers/Mode/HTTPController.cs | 14 +-
Netch/Controllers/Mode/NFController.cs | 22 +-
Netch/Controllers/Mode/TUNTAPController.cs | 12 +-
Netch/Controllers/PrivoxyController.cs | 6 +-
Netch/Forms/MainForm.Control.cs | 2 +-
Netch/Forms/MainForm.Designer.cs | 52 +-
Netch/Forms/MainForm.MenuStrip.cs | 41 +-
Netch/Forms/MainForm.Profile.cs | 4 +-
Netch/Forms/MainForm.Server_Mode.cs | 18 +
Netch/Forms/MainForm.Status.cs | 3 +-
Netch/Forms/MainForm.cs | 54 +-
Netch/Forms/Mode/Process.cs | 1 +
Netch/Forms/SettingForm.cs | 2 +-
Netch/Global.cs | 137 +---
Netch/Models/IServerUtil.cs | 42 ++
Netch/Models/LegacyServer.cs | 115 ---
Netch/Models/LegacySetting.cs | 60 --
Netch/Models/Server.cs | 99 +--
.../ServerController.cs} | 7 +-
.../WinFW/NetworkConnectionCollection.cs | 3 +-
Netch/NativeMethods.cs | 12 -
Netch/Netch.csproj | 3 -
Netch/Resources/zh-CN | 1 +
.../Form/ShadowsocksForm.Designer.cs} | 8 +-
.../Shadowsocks/Form/ShadowsocksForm.cs} | 45 +-
.../Shadowsocks/Form/ShadowsocksForm.resx} | 0
.../Shadowsocks}/Models/SSD/Main.cs | 6 +-
.../Shadowsocks/Models/SSD/SSDServer.cs} | 6 +-
.../Shadowsocks/Models/ShadowsocksConfig.cs} | 4 +-
Netch/ServerEx/Shadowsocks/SSController.cs | 95 +++
Netch/ServerEx/Shadowsocks/SSUtil.cs | 185 +++++
Netch/ServerEx/Shadowsocks/Shadowsocks.cs | 62 ++
.../Form/ShadowsocksRForm.Designer.cs} | 8 +-
.../ShadowsocksR/Form/ShadowsocksRForm.cs} | 53 +-
.../ShadowsocksR/Form/ShadowsocksRForm.resx} | 0
.../ShadowsocksR}/SSRController.cs | 13 +-
Netch/ServerEx/ShadowsocksR/SSRUtil.cs | 144 ++++
Netch/ServerEx/ShadowsocksR/ShadowsocksR.cs | 100 +++
.../Socks5/Form/Socks5Form.Designer.cs} | 8 +-
.../Socks5/Form/Socks5Form.cs} | 45 +-
.../Socks5/Form/Socks5Form.resx} | 0
Netch/ServerEx/Socks5/S5Util.cs | 81 +++
Netch/ServerEx/Socks5/Socks5.cs | 22 +
.../Trojan/Form/TrojanForm.Designer.cs} | 10 +-
.../Trojan/Form/TrojanForm.cs} | 45 +-
.../Trojan/Form/TrojanForm.resx} | 0
.../Trojan/Models/TrojanConfig.cs} | 8 +-
Netch/ServerEx/Trojan/Trojan.cs | 22 +
.../Trojan}/TrojanController.cs | 10 +-
Netch/ServerEx/Trojan/TrojanUtil.cs | 102 +++
.../VMess/Form/VMessForm.Designer.cs} | 8 +-
.../VMess/Form/VMessForm.cs} | 63 +-
.../VMess/Form/VMessForm.resx} | 0
.../VMess/Models/VMessConfig.cs} | 6 +-
.../VMess/Models/VMessJObject.cs} | 6 +-
Netch/ServerEx/VMess/VMess.cs | 118 +++
.../VMess}/VMessController.cs | 71 +-
Netch/ServerEx/VMess/VMessUtil.cs | 151 ++++
Netch/Utils/Bandwidth.cs | 4 +-
Netch/Utils/Configuration.cs | 70 +-
Netch/Utils/Extensions.cs | 23 -
Netch/Utils/Firewall.cs | 2 -
Netch/Utils/OnlyInstance.cs | 2 +-
Netch/Utils/Servers.cs | 51 ++
Netch/Utils/ShareLink.cs | 674 ++++--------------
Netch/Utils/TUNTAP.cs | 27 +
Netch/Utils/Utils.cs | 27 +-
Netch/Utils/WMI.cs | 20 -
72 files changed, 1661 insertions(+), 1595 deletions(-)
delete mode 100644 Netch/Controllers/EncryptedProxy/SSController.cs
create mode 100644 Netch/Models/IServerUtil.cs
delete mode 100644 Netch/Models/LegacyServer.cs
delete mode 100644 Netch/Models/LegacySetting.cs
rename Netch/{Controllers/Interface/EncryptedProxy.cs => Models/ServerController.cs} (86%)
rename Netch/{Forms/Server/Shadowsocks.Designer.cs => ServerEx/Shadowsocks/Form/ShadowsocksForm.Designer.cs} (98%)
rename Netch/{Forms/Server/Shadowsocks.cs => ServerEx/Shadowsocks/Form/ShadowsocksForm.cs} (58%)
rename Netch/{Forms/Server/Shadowsocks.resx => ServerEx/Shadowsocks/Form/ShadowsocksForm.resx} (100%)
rename Netch/{ => ServerEx/Shadowsocks}/Models/SSD/Main.cs (89%)
rename Netch/{Models/SSD/Server.cs => ServerEx/Shadowsocks/Models/SSD/SSDServer.cs} (90%)
rename Netch/{Models/SS/ShadowsocksServer.cs => ServerEx/Shadowsocks/Models/ShadowsocksConfig.cs} (80%)
create mode 100644 Netch/ServerEx/Shadowsocks/SSController.cs
create mode 100644 Netch/ServerEx/Shadowsocks/SSUtil.cs
create mode 100644 Netch/ServerEx/Shadowsocks/Shadowsocks.cs
rename Netch/{Forms/Server/ShadowsocksR.Designer.cs => ServerEx/ShadowsocksR/Form/ShadowsocksRForm.Designer.cs} (99%)
rename Netch/{Forms/Server/ShadowsocksR.cs => ServerEx/ShadowsocksR/Form/ShadowsocksRForm.cs} (56%)
rename Netch/{Forms/Server/ShadowsocksR.resx => ServerEx/ShadowsocksR/Form/ShadowsocksRForm.resx} (100%)
rename Netch/{Controllers/EncryptedProxy => ServerEx/ShadowsocksR}/SSRController.cs (80%)
create mode 100644 Netch/ServerEx/ShadowsocksR/SSRUtil.cs
create mode 100644 Netch/ServerEx/ShadowsocksR/ShadowsocksR.cs
rename Netch/{Forms/Server/Socks5.Designer.cs => ServerEx/Socks5/Form/Socks5Form.Designer.cs} (98%)
rename Netch/{Forms/Server/Socks5.cs => ServerEx/Socks5/Form/Socks5Form.cs} (54%)
rename Netch/{Forms/Server/Socks5.resx => ServerEx/Socks5/Form/Socks5Form.resx} (100%)
create mode 100644 Netch/ServerEx/Socks5/S5Util.cs
create mode 100644 Netch/ServerEx/Socks5/Socks5.cs
rename Netch/{Forms/Server/Trojan.Designer.cs => ServerEx/Trojan/Form/TrojanForm.Designer.cs} (97%)
rename Netch/{Forms/Server/Trojan.cs => ServerEx/Trojan/Form/TrojanForm.cs} (52%)
rename Netch/{Forms/Server/Trojan.resx => ServerEx/Trojan/Form/TrojanForm.resx} (100%)
rename Netch/{Models/Trojan.cs => ServerEx/Trojan/Models/TrojanConfig.cs} (96%)
create mode 100644 Netch/ServerEx/Trojan/Trojan.cs
rename Netch/{Controllers/EncryptedProxy => ServerEx/Trojan}/TrojanController.cs (78%)
create mode 100644 Netch/ServerEx/Trojan/TrojanUtil.cs
rename Netch/{Forms/Server/Vmess.Designer.cs => ServerEx/VMess/Form/VMessForm.Designer.cs} (99%)
rename Netch/{Forms/Server/Vmess.cs => ServerEx/VMess/Form/VMessForm.cs} (60%)
rename Netch/{Forms/Server/Vmess.resx => ServerEx/VMess/Form/VMessForm.resx} (100%)
rename Netch/{Models/Information/VMess.cs => ServerEx/VMess/Models/VMessConfig.cs} (98%)
rename Netch/{Models/VMess.cs => ServerEx/VMess/Models/VMessJObject.cs} (95%)
create mode 100644 Netch/ServerEx/VMess/VMess.cs
rename Netch/{Controllers/EncryptedProxy => ServerEx/VMess}/VMessController.cs (73%)
create mode 100644 Netch/ServerEx/VMess/VMessUtil.cs
delete mode 100644 Netch/Utils/Extensions.cs
create mode 100644 Netch/Utils/Servers.cs
delete mode 100644 Netch/Utils/WMI.cs
diff --git a/Netch/Controllers/EncryptedProxy/SSController.cs b/Netch/Controllers/EncryptedProxy/SSController.cs
deleted file mode 100644
index 46337e7a..00000000
--- a/Netch/Controllers/EncryptedProxy/SSController.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System.Text;
-using Netch.Models;
-using Netch.Utils;
-
-namespace Netch.Controllers
-{
- public class SSController : EncryptedProxy
- {
- private bool dllFlag;
-
- public SSController()
- {
- Name = "Shadowsocks";
- MainFile = "Shadowsocks.exe";
- StartedKeywords.Add("listening at");
- StoppedKeywords.AddRange(new[] {"Invalid config path", "usage", "plugin service exit unexpectedly"});
- }
-
- public override bool Start(Server server, Mode mode)
- {
- //从DLL启动Shaowsocks
- if (Global.Settings.BootShadowsocksFromDLL && (mode.Type == 0 || mode.Type == 1 || mode.Type == 2))
- {
- dllFlag = true;
- State = State.Starting;
- var client = Encoding.UTF8.GetBytes($"{LocalAddress}:{Socks5LocalPort}");
- var remote = Encoding.UTF8.GetBytes($"{server.Hostname}:{server.Port}");
- var passwd = Encoding.UTF8.GetBytes($"{server.Password}");
- var method = Encoding.UTF8.GetBytes($"{server.EncryptMethod}");
- if (!NativeMethods.Shadowsocks.Info(client, remote, passwd, method))
- {
- State = State.Stopped;
- Logging.Error("DLL SS INFO 设置失败!");
- return false;
- }
-
- Logging.Info("DLL SS INFO 设置成功!");
-
- if (!NativeMethods.Shadowsocks.Start())
- {
- State = State.Stopped;
- Logging.Error("DLL SS 启动失败!");
- return false;
- }
-
- Logging.Info("DLL SS 启动成功!");
- State = State.Started;
- return true;
- }
-
- #region Argument
-
- var argument = new StringBuilder();
- argument.Append(
- $"-s {server.Hostname} -p {server.Port} -b {LocalAddress} -l {Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u");
- if (!string.IsNullOrWhiteSpace(server.Plugin) && !string.IsNullOrWhiteSpace(server.PluginOption))
- argument.Append($" --plugin {server.Plugin} --plugin-opts \"{server.PluginOption}\"");
- if (mode.BypassChina) argument.Append(" --acl default.acl");
-
- #endregion
-
- return StartInstanceAuto(argument.ToString());
- }
-
- ///
- /// SSController 停止
- ///
- public override void Stop()
- {
- if (dllFlag) NativeMethods.Shadowsocks.Stop();
- else
- StopInstance();
- }
- }
-}
\ No newline at end of file
diff --git a/Netch/Controllers/ICSController.cs b/Netch/Controllers/ICSController.cs
index 0e668579..fd7fbe04 100644
--- a/Netch/Controllers/ICSController.cs
+++ b/Netch/Controllers/ICSController.cs
@@ -51,7 +51,7 @@ namespace Netch.Controllers
ushort[] gatewayMetric = null;
string[] dns = null;
- var outboundWmi = WMI.GetManagementObjectByDeviceNameOrDefault(Global.Outbound.Adapter.Description);
+ var outboundWmi = GetManagementObjectByDeviceNameOrDefault(Global.Outbound.Adapter.Description);
if (outboundWmi == null)
{
@@ -167,5 +167,18 @@ namespace Netch.Controllers
entry.Put(options);
}
}
+
+ public static ManagementObject GetManagementObjectByDeviceNameOrDefault(string deviceName)
+ {
+ foreach (ManagementObject mo in new ManagementClass("Win32_NetworkAdapterConfiguration").GetInstances())
+ {
+ if (((string) mo["Caption"]).EndsWith(deviceName))
+ {
+ return mo;
+ }
+ }
+
+ return null;
+ }
}
}
\ No newline at end of file
diff --git a/Netch/Controllers/Interface/ModeController.cs b/Netch/Controllers/Interface/ModeController.cs
index 696320af..8958834a 100644
--- a/Netch/Controllers/Interface/ModeController.cs
+++ b/Netch/Controllers/Interface/ModeController.cs
@@ -7,9 +7,11 @@ namespace Netch.Controllers
///
/// 启动
///
- /// 服务器
+ /// 服务器
/// 模式
/// 是否成功
- public abstract bool Start(Server server, Mode mode);
+ public abstract bool Start(Server s, Mode mode);
+
+ public abstract bool TestNatRequired { get; }
}
}
\ No newline at end of file
diff --git a/Netch/Controllers/MainController.cs b/Netch/Controllers/MainController.cs
index 9ebfcccd..eb962a0f 100644
--- a/Netch/Controllers/MainController.cs
+++ b/Netch/Controllers/MainController.cs
@@ -10,7 +10,7 @@ namespace Netch.Controllers
{
public static class MainController
{
- public static EncryptedProxy EncryptedProxyController { get; private set; }
+ public static ServerController ServerController { get; private set; }
public static ModeController ModeController { get; private set; }
public static bool NttTested;
@@ -27,7 +27,7 @@ namespace Netch.Controllers
{
Logging.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}");
- if (server.Type == "Socks5" && mode.Type == 4)
+ if (server.IsSocks5() && mode.Type == 4)
{
return false;
}
@@ -54,15 +54,8 @@ namespace Netch.Controllers
throw new StartFailedException();
}
- // 成功启动
- switch (mode.Type)
- {
- case 0:
- case 1:
- case 2:
- NatTest();
- break;
- }
+ if (ModeController.TestNatRequired)
+ NatTest();
return true;
}
@@ -97,32 +90,18 @@ namespace Netch.Controllers
private static async Task StartServer(Server server, Mode mode)
{
- switch (server.Type)
+ if (server.IsSocks5())
{
- case "Socks5":
- return true;
- case "SS":
- EncryptedProxyController = new SSController();
- break;
- case "SSR":
- EncryptedProxyController = new SSRController();
- break;
- case "VMess":
- EncryptedProxyController = new VMessController();
- break;
- case "Trojan":
- EncryptedProxyController = new TrojanController();
- break;
- default:
- Logging.Error("未知服务器类型");
- return false;
+ return true;
}
- Utils.Utils.KillProcessByName(EncryptedProxyController.MainFile);
+ ServerController = Servers.GetUtilByTypeName(server.Type).GetController();
+
+ Utils.Utils.KillProcessByName(ServerController.MainFile);
PortCheckAndShowMessageBox(Global.Settings.Socks5LocalPort, "Socks5");
- Global.MainForm.StatusText(i18N.Translate("Starting ", EncryptedProxyController.Name));
- if (await Task.Run(() => EncryptedProxyController.Start(server, mode)))
+ Global.MainForm.StatusText(i18N.Translate("Starting ", ServerController.Name));
+ if (await Task.Run(() => ServerController.Start(server, mode)))
{
UsingPorts.Add(StatusPortInfoText.Socks5Port = Global.Settings.Socks5LocalPort);
StatusPortInfoText.ShareLan = Global.Settings.LocalAddress == "0.0.0.0";
@@ -186,7 +165,7 @@ namespace Netch.Controllers
var tasks = new[]
{
- Task.Run(() => EncryptedProxyController?.Stop()),
+ Task.Run(() => ServerController?.Stop()),
Task.Run(() => ModeController?.Stop()),
};
await Task.WhenAll(tasks);
diff --git a/Netch/Controllers/Mode/HTTPController.cs b/Netch/Controllers/Mode/HTTPController.cs
index 87bd61ee..47be4084 100644
--- a/Netch/Controllers/Mode/HTTPController.cs
+++ b/Netch/Controllers/Mode/HTTPController.cs
@@ -4,12 +4,15 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Win32;
using Netch.Models;
+using Netch.ServerEx.Socks5;
using Netch.Utils;
namespace Netch.Controllers
{
public class HTTPController : ModeController
{
+ public override bool TestNatRequired { get; } = false;
+
public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
///
@@ -28,24 +31,25 @@ namespace Netch.Controllers
///
/// 启动
///
- /// 服务器
+ /// 服务器
/// 模式
/// 是否启动成功
- public override bool Start(Server server, Mode mode)
+ public override bool Start(Server s, Mode mode)
{
RecordPrevious();
try
{
- if (server.Type == "Socks5")
+ if (s.IsSocks5())
{
+ var server = (Socks5) s;
if (!string.IsNullOrWhiteSpace(server.Username) && !string.IsNullOrWhiteSpace(server.Password)) return false;
- pPrivoxyController.Start(server, mode);
+ pPrivoxyController.Start(s, mode);
}
else
{
- pPrivoxyController.Start(server, mode);
+ pPrivoxyController.Start(s, mode);
}
if (mode.Type == 3) NativeMethods.SetGlobal($"127.0.0.1:{Global.Settings.HTTPLocalPort}", IEProxyExceptions);
diff --git a/Netch/Controllers/Mode/NFController.cs b/Netch/Controllers/Mode/NFController.cs
index cb1ab0b8..8876f1b2 100644
--- a/Netch/Controllers/Mode/NFController.cs
+++ b/Netch/Controllers/Mode/NFController.cs
@@ -11,6 +11,8 @@ namespace Netch.Controllers
{
public class NFController : ModeController
{
+ public override bool TestNatRequired { get; } = true;
+
private static readonly ServiceController NFService = new ServiceController("netfilter2");
private static readonly string BinDriver = string.Empty;
@@ -45,7 +47,7 @@ namespace Netch.Controllers
Name = "Redirector";
}
- public override bool Start(Server server, Mode mode)
+ public override bool Start(Server s, Mode mode)
{
Logging.Info("内置驱动版本: " + Utils.Utils.FileVersion(BinDriver));
if (Utils.Utils.FileVersion(SystemDriver) != Utils.Utils.FileVersion(BinDriver))
@@ -69,22 +71,22 @@ namespace Netch.Controllers
aio_dial((int) NameList.TYPE_ADDNAME, "NTT.exe");
- if (server.Type != "Socks5")
+ if (s.IsSocks5())
{
- aio_dial((int) NameList.TYPE_TCPHOST, $"127.0.0.1:{Global.Settings.Socks5LocalPort}");
- aio_dial((int) NameList.TYPE_UDPHOST, $"127.0.0.1:{Global.Settings.Socks5LocalPort}");
- }
- else
- {
- var result = DNS.Lookup(server.Hostname);
+ var result = DNS.Lookup(s.Hostname);
if (result == null)
{
Logging.Info("无法解析服务器 IP 地址");
return false;
}
- aio_dial((int) NameList.TYPE_TCPHOST, $"{result}:{server.Port}");
- aio_dial((int) NameList.TYPE_UDPHOST, $"{result}:{server.Port}");
+ aio_dial((int) NameList.TYPE_TCPHOST, $"{result}:{s.Port}");
+ aio_dial((int) NameList.TYPE_UDPHOST, $"{result}:{s.Port}");
+ }
+ else
+ {
+ aio_dial((int) NameList.TYPE_TCPHOST, $"127.0.0.1:{Global.Settings.Socks5LocalPort}");
+ aio_dial((int) NameList.TYPE_UDPHOST, $"127.0.0.1:{Global.Settings.Socks5LocalPort}");
}
if (Global.Settings.ModifySystemDNS)
diff --git a/Netch/Controllers/Mode/TUNTAPController.cs b/Netch/Controllers/Mode/TUNTAPController.cs
index 8eb97931..b22ba467 100644
--- a/Netch/Controllers/Mode/TUNTAPController.cs
+++ b/Netch/Controllers/Mode/TUNTAPController.cs
@@ -16,6 +16,8 @@ namespace Netch.Controllers
{
public class TUNTAPController : ModeController
{
+ public override bool TestNatRequired { get; } = true;
+
// ByPassLan IP
private readonly List _bypassLanIPs = new List
{"10.0.0.0/8", "172.16.0.0/16", "192.168.0.0/16"};
@@ -198,10 +200,10 @@ namespace Netch.Controllers
return true;
}
- public override bool Start(Server server, Mode mode)
+ public override bool Start(Server s, Mode mode)
{
_savedMode = mode;
- _savedServer = server;
+ _savedServer = s;
if (!Configure()) return false;
@@ -229,8 +231,8 @@ namespace Netch.Controllers
}
var argument = new StringBuilder();
- if (server.Type == "Socks5")
- argument.Append($"-proxyServer {server.Hostname}:{server.Port} ");
+ if (s.IsSocks5())
+ argument.Append($"-proxyServer {s.Hostname}:{s.Port} ");
else
argument.Append($"-proxyServer 127.0.0.1:{Global.Settings.Socks5LocalPort} ");
@@ -303,7 +305,7 @@ namespace Netch.Controllers
if (MessageBoxX.Show(i18N.Translate("TUN/TAP driver is not detected. Is it installed now?"),
confirm: true) == DialogResult.OK)
{
- Configuration.addtap();
+ TUNTAP.addtap();
// 给点时间,不然立马安装完毕就查找适配器可能会导致找不到适配器ID
Thread.Sleep(1000);
if (string.IsNullOrEmpty(Global.TUNTAP.ComponentID = TUNTAP.GetComponentID()))
diff --git a/Netch/Controllers/PrivoxyController.cs b/Netch/Controllers/PrivoxyController.cs
index 7f6ecf84..2b5df91c 100644
--- a/Netch/Controllers/PrivoxyController.cs
+++ b/Netch/Controllers/PrivoxyController.cs
@@ -14,13 +14,11 @@ namespace Netch.Controllers
public bool Start(Server server, Mode mode)
{
- var isSocks5 = server.Type == "Socks5";
- var socks5Port = isSocks5 ? server.Port : Global.Settings.Socks5LocalPort;
var text = File.ReadAllText("bin\\default.conf")
.Replace("_BIND_PORT_", Global.Settings.HTTPLocalPort.ToString())
- .Replace("_DEST_PORT_", socks5Port.ToString())
+ .Replace("_DEST_PORT_", (server.IsSocks5() ? server.Port : Global.Settings.Socks5LocalPort).ToString())
.Replace("0.0.0.0", Global.Settings.LocalAddress);
- if (isSocks5)
+ if (server.IsSocks5())
text = text.Replace("/ 127.0.0.1", $"/ {server.Hostname}");
File.WriteAllText("data\\privoxy.conf", text);
diff --git a/Netch/Forms/MainForm.Control.cs b/Netch/Forms/MainForm.Control.cs
index 6d310396..d67d9996 100644
--- a/Netch/Forms/MainForm.Control.cs
+++ b/Netch/Forms/MainForm.Control.cs
@@ -22,7 +22,7 @@ namespace Netch.Forms
if (State == State.Waiting || State == State.Stopped)
{
// 服务器、模式 需选择
- if (!(ServerComboBox.SelectedItem is Models.Server server))
+ if (!(ServerComboBox.SelectedItem is Server server))
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
diff --git a/Netch/Forms/MainForm.Designer.cs b/Netch/Forms/MainForm.Designer.cs
index 473ddea1..ded9318e 100644
--- a/Netch/Forms/MainForm.Designer.cs
+++ b/Netch/Forms/MainForm.Designer.cs
@@ -33,11 +33,6 @@
this.MenuStrip = new System.Windows.Forms.MenuStrip();
this.ServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ImportServersFromClipboardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.AddSocks5ServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.AddShadowsocksServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.AddShadowsocksRServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.AddVMessServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.AddTrojanServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CreateProcessModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ReloadModesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -129,12 +124,7 @@
// ServerToolStripMenuItem
//
this.ServerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.ImportServersFromClipboardToolStripMenuItem,
- this.AddSocks5ServerToolStripMenuItem,
- this.AddShadowsocksServerToolStripMenuItem,
- this.AddShadowsocksRServerToolStripMenuItem,
- this.AddVMessServerToolStripMenuItem,
- this.AddTrojanServerToolStripMenuItem});
+ this.ImportServersFromClipboardToolStripMenuItem});
this.ServerToolStripMenuItem.Margin = new System.Windows.Forms.Padding(3, 0, 0, 1);
this.ServerToolStripMenuItem.Name = "ServerToolStripMenuItem";
this.ServerToolStripMenuItem.Size = new System.Drawing.Size(57, 21);
@@ -147,41 +137,6 @@
this.ImportServersFromClipboardToolStripMenuItem.Text = "Import Servers From Clipboard";
this.ImportServersFromClipboardToolStripMenuItem.Click += new System.EventHandler(this.ImportServersFromClipboardToolStripMenuItem_Click);
//
- // AddSocks5ServerToolStripMenuItem
- //
- this.AddSocks5ServerToolStripMenuItem.Name = "AddSocks5ServerToolStripMenuItem";
- this.AddSocks5ServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
- this.AddSocks5ServerToolStripMenuItem.Text = "Add [Socks5] Server";
- this.AddSocks5ServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
- //
- // AddShadowsocksServerToolStripMenuItem
- //
- this.AddShadowsocksServerToolStripMenuItem.Name = "AddShadowsocksServerToolStripMenuItem";
- this.AddShadowsocksServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
- this.AddShadowsocksServerToolStripMenuItem.Text = "Add [Shadowsocks] Server";
- this.AddShadowsocksServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
- //
- // AddShadowsocksRServerToolStripMenuItem
- //
- this.AddShadowsocksRServerToolStripMenuItem.Name = "AddShadowsocksRServerToolStripMenuItem";
- this.AddShadowsocksRServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
- this.AddShadowsocksRServerToolStripMenuItem.Text = "Add [ShadowsocksR] Server";
- this.AddShadowsocksRServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
- //
- // AddVMessServerToolStripMenuItem
- //
- this.AddVMessServerToolStripMenuItem.Name = "AddVMessServerToolStripMenuItem";
- this.AddVMessServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
- this.AddVMessServerToolStripMenuItem.Text = "Add [VMess] Server";
- this.AddVMessServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
- //
- // AddTrojanServerToolStripMenuItem
- //
- this.AddTrojanServerToolStripMenuItem.Name = "AddTrojanServerToolStripMenuItem";
- this.AddTrojanServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
- this.AddTrojanServerToolStripMenuItem.Text = "Add [Trojan] Server";
- this.AddTrojanServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
- //
// ModeToolStripMenuItem
//
this.ModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
@@ -751,11 +706,6 @@
}
private System.Windows.Forms.ToolStripButton AboutToolStripButton;
- private System.Windows.Forms.ToolStripMenuItem AddShadowsocksRServerToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem AddShadowsocksServerToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem AddSocks5ServerToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem AddTrojanServerToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem AddVMessServerToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem CleanDNSCacheToolStripMenuItem;
private System.Windows.Forms.TableLayoutPanel configLayoutPanel;
private System.Windows.Forms.GroupBox ConfigurationGroupBox;
diff --git a/Netch/Forms/MainForm.MenuStrip.cs b/Netch/Forms/MainForm.MenuStrip.cs
index 065f5a1e..ca3dafa3 100644
--- a/Netch/Forms/MainForm.MenuStrip.cs
+++ b/Netch/Forms/MainForm.MenuStrip.cs
@@ -6,11 +6,8 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Controllers;
using Netch.Forms.Mode;
-using Netch.Forms.Server;
using Netch.Models;
using Netch.Utils;
-using Trojan = Netch.Forms.Server.Trojan;
-using VMess = Netch.Forms.Server.VMess;
namespace Netch.Forms
{
@@ -29,7 +26,7 @@ namespace Netch.Forms
var texts = Clipboard.GetText();
if (!string.IsNullOrWhiteSpace(texts))
{
- var result = ShareLink.Parse(texts);
+ var result = ShareLink.ParseText(texts);
if (result != null)
{
@@ -50,18 +47,15 @@ namespace Netch.Forms
private void AddServerToolStripMenuItem_Click(object sender, EventArgs e)
{
- Form form = ((ToolStripMenuItem) sender).Name switch
- {
- "AddSocks5ServerToolStripMenuItem" => new Socks5(),
- "AddShadowsocksServerToolStripMenuItem" => new Shadowsocks(),
- "AddShadowsocksRServerToolStripMenuItem" => new ShadowsocksR(),
- "AddVMessServerToolStripMenuItem" => new VMess(),
- "AddTrojanServerToolStripMenuItem" => new Trojan(),
- _ => null
- };
+ var s = ((ToolStripMenuItem) sender).Text;
+
+ var start = s.IndexOf("[", StringComparison.Ordinal) + 1;
+ var end = s.IndexOf("]", start, StringComparison.Ordinal);
+ var result = s.Substring(start, end - start);
Hide();
- form?.ShowDialog();
+ Servers.GetUtilByFullName(result).Create();
+
InitServer();
Configuration.Save();
Show();
@@ -147,7 +141,7 @@ namespace Netch.Forms
Remark = "ProxyUpdate",
Type = 5
};
- await MainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
+ await MainController.Start(ServerComboBox.SelectedItem as Server, mode);
}
var serverLock = new object();
@@ -164,20 +158,11 @@ namespace Netch.Forms
var str = await WebUtil.DownloadStringAsync(request);
- try
- {
- str = ShareLink.URLSafeBase64Decode(str);
- }
- catch
- {
- // ignored
- }
-
lock (serverLock)
{
Global.Settings.Server.RemoveAll(server => server.Group == item.Remark);
- var result = ShareLink.Parse(str);
+ var result = ShareLink.ParseText(str);
if (result != null)
{
foreach (var server in result)
@@ -300,7 +285,7 @@ namespace Netch.Forms
Type = 5
};
State = State.Starting;
- await MainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
+ await MainController.Start(ServerComboBox.SelectedItem as Server, mode);
}
var req = WebUtil.CreateRequest(Global.Settings.ACL);
@@ -355,8 +340,8 @@ namespace Netch.Forms
{
await Task.Run(() =>
{
- Configuration.deltapall();
- Configuration.addtap();
+ TUNTAP.deltapall();
+ TUNTAP.addtap();
});
StatusText(i18N.Translate("Reinstall TUN/TAP driver successfully"));
}
diff --git a/Netch/Forms/MainForm.Profile.cs b/Netch/Forms/MainForm.Profile.cs
index 8ea02d58..753f4986 100644
--- a/Netch/Forms/MainForm.Profile.cs
+++ b/Netch/Forms/MainForm.Profile.cs
@@ -91,7 +91,7 @@ namespace Netch.Forms
if (p.IsDummy)
throw new Exception("Profile not found.");
- var server = ServerComboBox.Items.Cast().FirstOrDefault(s => s.Remark.Equals(p.ServerRemark));
+ var server = ServerComboBox.Items.Cast().FirstOrDefault(s => s.Remark.Equals(p.ServerRemark));
var mode = ModeComboBox.Items.Cast().FirstOrDefault(m => m.Remark.Equals(p.ModeRemark));
if (server == null)
@@ -110,7 +110,7 @@ namespace Netch.Forms
private void SaveProfile(int index)
{
- var selectedServer = (Models.Server) ServerComboBox.SelectedItem;
+ var selectedServer = (Server) ServerComboBox.SelectedItem;
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
var name = ProfileNameText.Text;
diff --git a/Netch/Forms/MainForm.Server_Mode.cs b/Netch/Forms/MainForm.Server_Mode.cs
index 139457dd..88939a66 100644
--- a/Netch/Forms/MainForm.Server_Mode.cs
+++ b/Netch/Forms/MainForm.Server_Mode.cs
@@ -1,7 +1,9 @@
using System;
using System.Drawing;
+using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
+using Netch.Utils;
namespace Netch.Forms
{
@@ -152,5 +154,21 @@ namespace Netch.Forms
// ignored
}
}
+
+ private void AddAddServerToolStripMenuItems()
+ {
+ foreach (var serversUtil in Servers.ServerUtils.Where(i => !string.IsNullOrEmpty(i.FullName)))
+ {
+ var fullName = serversUtil.FullName;
+ var control = new ToolStripMenuItem
+ {
+ Name = $"Add{fullName}ServerToolStripMenuItem",
+ Size = new Size(259, 22),
+ Text = i18N.TranslateFormat("Add [{0}] Server", fullName),
+ };
+ control.Click += AddServerToolStripMenuItem_Click;
+ ServerToolStripMenuItem.DropDownItems.Add(control);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Netch/Forms/MainForm.Status.cs b/Netch/Forms/MainForm.Status.cs
index 35596997..1c7700e5 100644
--- a/Netch/Forms/MainForm.Status.cs
+++ b/Netch/Forms/MainForm.Status.cs
@@ -1,7 +1,6 @@
using System;
using System.Drawing;
using System.Text;
-using Netch.Controllers;
using Netch.Models;
using Netch.Utils;
@@ -13,6 +12,8 @@ namespace Netch.Forms
partial class MainForm
{
+ private bool IsWaiting => State == State.Waiting || State == State.Stopped;
+
private State _state = State.Waiting;
///
diff --git a/Netch/Forms/MainForm.cs b/Netch/Forms/MainForm.cs
index 0388b807..51b17ae6 100644
--- a/Netch/Forms/MainForm.cs
+++ b/Netch/Forms/MainForm.cs
@@ -5,17 +5,13 @@ using System.Windows.Forms;
using Microsoft.Win32;
using Netch.Controllers;
using Netch.Forms.Mode;
-using Netch.Forms.Server;
using Netch.Models;
using Netch.Utils;
-using Trojan = Netch.Forms.Server.Trojan;
-using VMess = Netch.Forms.Server.VMess;
namespace Netch.Forms
{
public partial class MainForm : Form
{
-
public MainForm()
{
InitializeComponent();
@@ -40,6 +36,8 @@ namespace Netch.Forms
private void MainForm_Load(object sender, EventArgs e)
{
+ AddAddServerToolStripMenuItems();
+
OnlyInstance.Called += OnCalled;
// 计算 ComboBox绘制 目标宽度
_eWidth = ServerComboBox.Width / 10;
@@ -180,11 +178,6 @@ namespace Netch.Forms
{
ServerToolStripMenuItem.Text = i18N.Translate("Server");
ImportServersFromClipboardToolStripMenuItem.Text = i18N.Translate("Import Servers From Clipboard");
- AddSocks5ServerToolStripMenuItem.Text = i18N.Translate("Add [Socks5] Server");
- AddShadowsocksServerToolStripMenuItem.Text = i18N.Translate("Add [Shadowsocks] Server");
- AddShadowsocksRServerToolStripMenuItem.Text = i18N.Translate("Add [ShadowsocksR] Server");
- AddVMessServerToolStripMenuItem.Text = i18N.Translate("Add [VMess] Server");
- AddTrojanServerToolStripMenuItem.Text = i18N.Translate("Add [Trojan] Server");
ModeToolStripMenuItem.Text = i18N.Translate("Mode");
HelpToolStripMenuItem.Text = i18N.Translate("Help");
CreateProcessModeToolStripMenuItem.Text = i18N.Translate("Create Process Mode");
@@ -227,7 +220,7 @@ namespace Netch.Forms
private void Exit(bool forceExit = false)
{
- if (State != State.Waiting && State != State.Stopped && !Global.Settings.StopWhenExited && !forceExit)
+ if (!IsWaiting && !Global.Settings.StopWhenExited && !forceExit)
{
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
@@ -237,9 +230,8 @@ namespace Netch.Forms
Hide();
NotifyIcon.Visible = false;
- if (State != State.Waiting && State != State.Stopped)
+ if (!IsWaiting)
{
- // 已启动
ControlFun();
}
@@ -282,17 +274,11 @@ namespace Netch.Forms
return;
}
- Form server = Global.Settings.Server[ServerComboBox.SelectedIndex].Type switch
- {
- "Socks5" => new Socks5(Global.Settings.Server[ServerComboBox.SelectedIndex]),
- "SS" => new Shadowsocks(Global.Settings.Server[ServerComboBox.SelectedIndex]),
- "SSR" => new ShadowsocksR(Global.Settings.Server[ServerComboBox.SelectedIndex]),
- "VMess" => new VMess(Global.Settings.Server[ServerComboBox.SelectedIndex]),
- "Trojan" => new Trojan(Global.Settings.Server[ServerComboBox.SelectedIndex]),
- _ => null
- };
Hide();
- server?.ShowDialog();
+
+ var server = Global.Settings.Server[ServerComboBox.SelectedIndex];
+ Servers.GetUtilByTypeName(server.Type).Edit(server);
+
InitServer();
Configuration.Save();
Show();
@@ -318,7 +304,7 @@ namespace Netch.Forms
private void EditModePictureBox_Click(object sender, EventArgs e)
{
// 当前ModeComboBox中至少有一项
- if (ModeComboBox.Items.Count <= 0 || ModeComboBox.SelectedIndex == -1)
+ if (ModeComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
return;
@@ -331,7 +317,6 @@ namespace Netch.Forms
{
Hide();
new Process(selectedMode).ShowDialog();
- InitMode();
Show();
break;
}
@@ -352,10 +337,7 @@ namespace Netch.Forms
return;
}
- var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
- ModeComboBox.Items.Remove(selectedMode);
- Modes.Delete(selectedMode);
-
+ Modes.Delete((Models.Mode) ModeComboBox.SelectedItem);
SelectLastMode();
}
@@ -368,11 +350,21 @@ namespace Netch.Forms
return;
}
- var selectedMode = (Models.Server) ServerComboBox.SelectedItem;
try
{
//听说巨硬BUG经常会炸,所以Catch一下 :D
- Clipboard.SetText(ShareLink.GetShareLink(selectedMode));
+ var server = (Server) ServerComboBox.SelectedItem;
+ string text;
+ if (ModifierKeys == Keys.Control)
+ {
+ text = ShareLink.GetNetchLink(server);
+ }
+ else
+ {
+ text = ShareLink.GetShareLink(server);
+ }
+
+ Clipboard.SetText(text);
}
catch (Exception)
{
@@ -391,7 +383,7 @@ namespace Netch.Forms
var index = ServerComboBox.SelectedIndex;
- Global.Settings.Server.Remove(ServerComboBox.SelectedItem as Models.Server);
+ Global.Settings.Server.Remove(ServerComboBox.SelectedItem as Server);
InitServer();
Configuration.Save();
diff --git a/Netch/Forms/Mode/Process.cs b/Netch/Forms/Mode/Process.cs
index b83f6d13..5234d6c3 100644
--- a/Netch/Forms/Mode/Process.cs
+++ b/Netch/Forms/Mode/Process.cs
@@ -215,6 +215,7 @@ namespace Netch.Forms.Mode
_mode.Rule.AddRange(RuleListBox.Items.Cast());
Modes.WriteFile(_mode);
+ Global.MainForm.InitMode();
Edited = false;
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
}
diff --git a/Netch/Forms/SettingForm.cs b/Netch/Forms/SettingForm.cs
index 5a44a94d..65760b42 100644
--- a/Netch/Forms/SettingForm.cs
+++ b/Netch/Forms/SettingForm.cs
@@ -156,7 +156,7 @@ namespace Netch.Forms
static void CheckPort(string portName, int port, int originPort, PortType portType = PortType.Both)
{
- if (port <= 0 || port >= 65536)
+ if (port <= 0 || port > 65536)
throw new FormatException();
if (port == originPort)
diff --git a/Netch/Global.cs b/Netch/Global.cs
index 1f10a127..72160ba5 100644
--- a/Netch/Global.cs
+++ b/Netch/Global.cs
@@ -11,14 +11,13 @@ namespace Netch
{
public static class Global
{
-
///
/// 换行
///
public const string EOF = "\r\n";
public static readonly string NetchDir = Application.StartupPath;
-
+
///
/// 主窗体的静态实例
///
@@ -26,138 +25,6 @@ namespace Netch
public static bool SupportFakeDns = false;
- ///
- /// SS/SSR 加密方式
- ///
- public static class EncryptMethods
- {
- ///
- /// SS 加密列表
- ///
- public static List SS = new List
- {
- "rc4-md5",
- "aes-128-gcm",
- "aes-192-gcm",
- "aes-256-gcm",
- "aes-128-cfb",
- "aes-192-cfb",
- "aes-256-cfb",
- "aes-128-ctr",
- "aes-192-ctr",
- "aes-256-ctr",
- "camellia-128-cfb",
- "camellia-192-cfb",
- "camellia-256-cfb",
- "bf-cfb",
- "chacha20-ietf-poly1305",
- "xchacha20-ietf-poly1305",
- "salsa20",
- "chacha20",
- "chacha20-ietf"
- };
-
- ///
- /// SSR 加密列表
- ///
- public static List SSR = new List
- {
- "none",
- "table",
- "rc4",
- "rc4-md5",
- "rc4-md5-6",
- "aes-128-cfb",
- "aes-192-cfb",
- "aes-256-cfb",
- "aes-128-ctr",
- "aes-192-ctr",
- "aes-256-ctr",
- "bf-cfb",
- "camellia-128-cfb",
- "camellia-192-cfb",
- "camellia-256-cfb",
- "cast5-cfb",
- "des-cfb",
- "idea-cfb",
- "rc2-cfb",
- "seed-cfb",
- "salsa20",
- "chacha20",
- "chacha20-ietf"
- };
- ///
- /// VMess 解密列表
- ///
- public static List VMess = new List
- {
- "auto",
- "none",
- "aes-128-gcm",
- "chacha20-poly1305"
- };
-
- ///
- /// VMess QUIC 加密列表
- ///
- public static List VMessQUIC = new List
- {
- "none",
- "aes-128-gcm",
- "chacha20-poly1305"
- };
- }
-
- ///
- /// SSR 协议列表
- ///
- public static List Protocols = new List
- {
- "origin",
- "verify_deflate",
- "auth_sha1_v4",
- "auth_aes128_md5",
- "auth_aes128_sha1",
- "auth_chain_a"
- };
-
- ///
- /// SSR 混淆列表
- ///
- public static List OBFSs = new List
- {
- "plain",
- "http_simple",
- "http_post",
- "tls1.2_ticket_auth"
- };
-
- ///
- /// V2Ray 传输协议
- ///
- public static List TransferProtocols = new List
- {
- "tcp",
- "kcp",
- "ws",
- "h2",
- "quic"
- };
-
- ///
- /// V2Ray 伪装类型
- ///
- public static List FakeTypes = new List
- {
- "none",
- "http",
- "srtp",
- "utp",
- "wechat-video",
- "dtls",
- "wireguard"
- };
-
///
/// 出口适配器
///
@@ -212,4 +79,4 @@ namespace Netch
///
public static readonly List Modes = new List();
}
-}
+}
\ No newline at end of file
diff --git a/Netch/Models/IServerUtil.cs b/Netch/Models/IServerUtil.cs
new file mode 100644
index 00000000..097d6ab0
--- /dev/null
+++ b/Netch/Models/IServerUtil.cs
@@ -0,0 +1,42 @@
+using System.Collections.Generic;
+using Newtonsoft.Json.Linq;
+
+namespace Netch.Models
+{
+ public interface IServerUtil
+ {
+ ///
+ /// Collection order basis
+ ///
+ ushort Priority { get; }
+
+ ///
+ /// Server.Type
+ ///
+ string TypeName { get; }
+
+ ///
+ /// Protocol Name
+ ///
+ string FullName { get; }
+
+ ///
+ /// Support URI
+ ///
+ string[] UriScheme { get; }
+
+ Server ParseJObject(JObject j);
+
+ public void Edit(Server s);
+
+ public void Create();
+
+ string GetShareLink(Server server);
+
+ public abstract ServerController GetController();
+
+ public abstract IEnumerable ParseUri(string text);
+
+ bool CheckServer(Server s);
+ }
+}
\ No newline at end of file
diff --git a/Netch/Models/LegacyServer.cs b/Netch/Models/LegacyServer.cs
deleted file mode 100644
index 87bebc8b..00000000
--- a/Netch/Models/LegacyServer.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-namespace Netch.Models
-{
- public class LegacyServer
- {
- ///
- /// 备注
- ///
- public string Remark;
-
- ///
- /// 组
- ///
- public string Group = "None";
-
- ///
- /// 类型(Socks5、Shadowsocks、ShadowsocksR、VMess)
- ///
- public string Type;
-
- ///
- /// 地址
- ///
- public string Address;
-
- ///
- /// 端口
- ///
- public int Port;
-
- ///
- /// 用户名
- ///
- public string Username;
-
- ///
- /// 密码
- ///
- public string Password;
-
- ///
- /// 用户 ID(V2)
- ///
- public string UserID = string.Empty;
-
- ///
- /// 额外 ID(V2)
- ///
- public int AlterID = 0;
-
- ///
- /// 加密方式
- ///
- public string EncryptMethod;
-
- ///
- /// 协议
- ///
- public string Protocol;
-
- ///
- /// 协议参数
- ///
- public string ProtocolParam;
-
- ///
- /// 混淆(SSR)/ 插件(SS)
- ///
- public string OBFS;
-
- ///
- /// 混淆参数(SSR)/ 插件参数(SS)
- ///
- public string OBFSParam;
-
- ///
- /// 传输协议(V2)
- ///
- public string TransferProtocol = "tcp";
-
- ///
- /// 伪装类型(V2)
- ///
- public string FakeType = string.Empty;
-
- ///
- /// 伪装域名(V2:HTTP、WebSocket、HTTP/2)
- ///
- public string Host = string.Empty;
-
- ///
- /// 传输路径(V2:WebSocket、HTTP/2)
- ///
- public string Path = string.Empty;
-
- ///
- /// QUIC 加密方式(V2)
- ///
- public string QUICSecurity = "none";
-
- ///
- /// QUIC 加密密钥(V2)
- ///
- public string QUICSecret = string.Empty;
-
- ///
- /// TLS 底层传输安全(V2)
- ///
- public bool TLSSecure = false;
-
- ///
- /// 延迟
- ///
- public int Delay = -1;
- }
-}
diff --git a/Netch/Models/LegacySetting.cs b/Netch/Models/LegacySetting.cs
deleted file mode 100644
index d99b6b9d..00000000
--- a/Netch/Models/LegacySetting.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using System.Collections.Generic;
-
-namespace Netch.Models
-{
- ///
- /// 用于读取和写入的配置的类
- ///
- public class LegacySetting
- {
- ///
- /// 服务器选择位置
- ///
- public int ServerComboBoxSelectedIndex = 0;
-
- ///
- /// 模式选择位置
- ///
- public int ModeComboBoxSelectedIndex = 0;
-
- ///
- /// HTTP 本地端口
- ///
- public int HTTPLocalPort = 2802;
-
- ///
- /// Socks5 本地端口
- ///
- public int Socks5LocalPort = 2801;
-
- ///
- /// HTTP 和 Socks5 本地代理地址
- ///
- public string LocalAddress = "127.0.0.1";
-
- ///
- /// Redirector TCP 占用端口
- ///
- public int RedirectorTCPPort = 2800;
-
- ///
- /// TUNTAP 适配器配置
- ///
- public TUNTAPConfig TUNTAP = new TUNTAPConfig();
-
- ///
- /// 服务器列表
- ///
- public List Server = new List();
-
- ///
- /// 订阅链接列表
- ///
- public List SubscribeLink = new List();
-
- ///
- /// 全局绕过 IP 列表
- ///
- public List BypassIPs = new List();
- }
-}
diff --git a/Netch/Models/Server.cs b/Netch/Models/Server.cs
index 92a9ddc4..18b90ee0 100644
--- a/Netch/Models/Server.cs
+++ b/Netch/Models/Server.cs
@@ -17,7 +17,7 @@ namespace Netch.Models
public string Group = "None";
///
- /// 代理类型(HTTP、HTTPS、Socks5、SS、SSR、VMess)
+ /// 代理类型
///
public string Type;
@@ -36,101 +36,6 @@ namespace Netch.Models
///
public int Port;
- ///
- /// 账号(HTTP、HTTPS、Socks5)
- ///
- public string Username;
-
- ///
- /// 密码(HTTP、HTTPS、Socks5、SS、SSR)
- ///
- public string Password;
-
- ///
- /// 用户 ID(VMess)
- ///
- public string UserID = string.Empty;
-
- ///
- /// 额外 ID(VMess)
- ///
- public int AlterID = 0;
-
- ///
- /// 加密方式(SS、SSR、VMess)
- ///
- public string EncryptMethod;
-
- ///
- /// 插件(SS)
- ///
- public string Plugin;
-
- ///
- /// 插件参数(SS)
- ///
- public string PluginOption;
-
- ///
- /// 协议(SSR)
- ///
- public string Protocol = Global.Protocols[0];
-
- ///
- /// 协议参数(SSR)
- ///
- public string ProtocolParam;
-
- ///
- /// 混淆(SSR)
- ///
- public string OBFS = Global.OBFSs[0];
-
- ///
- /// 混淆参数(SSR)
- ///
- public string OBFSParam;
-
- ///
- /// 传输协议(VMess)
- ///
- public string TransferProtocol = Global.TransferProtocols[0];
-
- ///
- /// 伪装类型(VMess)
- ///
- public string FakeType = Global.FakeTypes[0];
-
- ///
- /// 伪装域名(VMess:HTTP、WebSocket、HTTP/2)
- ///
- public string Host = string.Empty;
-
- ///
- /// 传输路径(VMess:WebSocket、HTTP/2)
- ///
- public string Path = string.Empty;
-
- ///
- /// QUIC 加密方式(VMess)
- ///
- public string QUICSecure = Global.EncryptMethods.VMessQUIC[0];
-
- ///
- /// QUIC 加密密钥(VMess)
- ///
- public string QUICSecret = string.Empty;
-
- ///
- /// TLS 底层传输安全(VMess)
- ///
- public bool TLSSecure = false;
-
- ///
- /// Mux 多路复用(VMess)
- ///
- public bool UseMux = true;
-
///
/// 延迟
///
@@ -141,6 +46,8 @@ namespace Netch.Models
///
public string Country;
+ public bool IsSocks5() => Type == "Socks5";
+
///
/// 获取备注
///
diff --git a/Netch/Controllers/Interface/EncryptedProxy.cs b/Netch/Models/ServerController.cs
similarity index 86%
rename from Netch/Controllers/Interface/EncryptedProxy.cs
rename to Netch/Models/ServerController.cs
index 5f3f4a6d..493bbb6f 100644
--- a/Netch/Controllers/Interface/EncryptedProxy.cs
+++ b/Netch/Models/ServerController.cs
@@ -1,8 +1,8 @@
-using Netch.Models;
+using Netch.Controllers;
-namespace Netch.Controllers
+namespace Netch.Models
{
- public abstract class EncryptedProxy : Controller
+ public abstract class ServerController : Controller
{
private int? _socks5Port;
@@ -27,5 +27,6 @@ namespace Netch.Controllers
/// 模式
/// 是否启动成功
public abstract bool Start(Server server, Mode mode);
+
}
}
\ No newline at end of file
diff --git a/Netch/Models/WinFW/NetworkConnectionCollection.cs b/Netch/Models/WinFW/NetworkConnectionCollection.cs
index 89ad9b3b..171510c4 100644
--- a/Netch/Models/WinFW/NetworkConnectionCollection.cs
+++ b/Netch/Models/WinFW/NetworkConnectionCollection.cs
@@ -1,7 +1,6 @@
using System.Collections;
using System.Linq;
-using System.Management;
-using NETCONLib;
+ using NETCONLib;
namespace WinFW
{
diff --git a/Netch/NativeMethods.cs b/Netch/NativeMethods.cs
index b7f7dff3..c1c521ba 100644
--- a/Netch/NativeMethods.cs
+++ b/Netch/NativeMethods.cs
@@ -64,18 +64,6 @@ namespace Netch
[DllImport("sysproxy", CallingConvention = CallingConvention.Cdecl)]
public static extern bool SetURL([MarshalAs(UnmanagedType.LPTStr)] string remote);
- public class Shadowsocks
- {
- [DllImport("shadowsocks-windows-dynamic", CallingConvention = CallingConvention.Cdecl)]
- public static extern bool Info(byte[] client, byte[] remote, byte[] passwd, byte[] method);
-
- [DllImport("shadowsocks-windows-dynamic", CallingConvention = CallingConvention.Cdecl)]
- public static extern bool Start();
-
- [DllImport("shadowsocks-windows-dynamic", CallingConvention = CallingConvention.Cdecl)]
- public static extern void Stop();
- }
-
[DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]
public static extern uint FlushDNSResolverCache();
}
diff --git a/Netch/Netch.csproj b/Netch/Netch.csproj
index 161fc70e..bc7bd024 100644
--- a/Netch/Netch.csproj
+++ b/Netch/Netch.csproj
@@ -87,9 +87,6 @@
-
- Form
-
True
True
diff --git a/Netch/Resources/zh-CN b/Netch/Resources/zh-CN
index 50856791..00127ba7 100644
--- a/Netch/Resources/zh-CN
+++ b/Netch/Resources/zh-CN
@@ -33,6 +33,7 @@
"Server": "服务器",
"Import Servers From Clipboard": "从剪贴板导入服务器",
"Import servers error!": "未找到可导入的链接!",
+ "Add [{0}] Server": "添加 [{0}] 服务器",
"Add [Socks5] Server": "添加 [Socks5] 服务器",
"Add [Shadowsocks] Server": "添加 [Shadowsocks] 服务器",
"Add [ShadowsocksR] Server": "添加 [ShadowsocksR] 服务器",
diff --git a/Netch/Forms/Server/Shadowsocks.Designer.cs b/Netch/ServerEx/Shadowsocks/Form/ShadowsocksForm.Designer.cs
similarity index 98%
rename from Netch/Forms/Server/Shadowsocks.Designer.cs
rename to Netch/ServerEx/Shadowsocks/Form/ShadowsocksForm.Designer.cs
index 6159e23a..a2dc534e 100644
--- a/Netch/Forms/Server/Shadowsocks.Designer.cs
+++ b/Netch/ServerEx/Shadowsocks/Form/ShadowsocksForm.Designer.cs
@@ -1,6 +1,6 @@
-namespace Netch.Forms.Server
+namespace Netch.ServerEx.Shadowsocks.Form
{
- partial class Shadowsocks
+ partial class ShadowsocksForm
{
///
/// Required designer variable.
@@ -28,7 +28,7 @@
///
private void InitializeComponent()
{
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Shadowsocks));
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ShadowsocksForm));
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
this.PluginOptionsTextBox = new System.Windows.Forms.TextBox();
this.PluginOptionsLabel = new System.Windows.Forms.Label();
@@ -215,7 +215,7 @@
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
- this.Name = "Shadowsocks";
+ this.Name = "ShadowsocksForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Shadowsocks";
this.Load += new System.EventHandler(this.Shadowsocks_Load);
diff --git a/Netch/Forms/Server/Shadowsocks.cs b/Netch/ServerEx/Shadowsocks/Form/ShadowsocksForm.cs
similarity index 58%
rename from Netch/Forms/Server/Shadowsocks.cs
rename to Netch/ServerEx/Shadowsocks/Form/ShadowsocksForm.cs
index 87615917..d32ee87e 100644
--- a/Netch/Forms/Server/Shadowsocks.cs
+++ b/Netch/ServerEx/Shadowsocks/Form/ShadowsocksForm.cs
@@ -1,19 +1,18 @@
using System;
-using System.Drawing;
using System.Windows.Forms;
using Netch.Utils;
-namespace Netch.Forms.Server
+namespace Netch.ServerEx.Shadowsocks.Form
{
- public partial class Shadowsocks : Form
+ public partial class ShadowsocksForm : System.Windows.Forms.Form
{
- private readonly Models.Server _server;
+ private readonly Shadowsocks _server;
- public Shadowsocks(Models.Server server = default)
+ public ShadowsocksForm(Shadowsocks server = default)
{
InitializeComponent();
- _server = server ?? new Models.Server {EncryptMethod = Global.EncryptMethods.SS[0]};
+ _server = server ?? new Shadowsocks();
}
private void Shadowsocks_Load(object sender, EventArgs e)
@@ -29,7 +28,7 @@ namespace Netch.Forms.Server
PluginOptionsLabel.Text = i18N.Translate(PluginOptionsLabel.Text);
ControlButton.Text = i18N.Translate(ControlButton.Text);
- EncryptMethodComboBox.Items.AddRange(Global.EncryptMethods.SS.ToArray());
+ EncryptMethodComboBox.Items.AddRange(SSGlobal.EncryptMethods.ToArray());
#endregion
@@ -37,43 +36,19 @@ namespace Netch.Forms.Server
AddressTextBox.Text = _server.Hostname;
PortTextBox.Text = _server.Port.ToString();
PasswordTextBox.Text = _server.Password;
- EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.SS.IndexOf(_server.EncryptMethod);
+ EncryptMethodComboBox.SelectedIndex = SSGlobal.EncryptMethods.IndexOf(_server.EncryptMethod);
PluginTextBox.Text = _server.Plugin;
PluginOptionsTextBox.Text = _server.PluginOption;
}
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
- if (sender is ComboBox cbx)
- {
- e.DrawBackground();
-
- if (e.Index >= 0)
- {
- var sf = new StringFormat
- {
- LineAlignment = StringAlignment.Center,
- Alignment = StringAlignment.Center
- };
-
- var brush = new SolidBrush(cbx.ForeColor);
-
- if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
- {
- brush = SystemBrushes.HighlightText as SolidBrush;
- }
-
- e.Graphics.DrawString(cbx.Items[e.Index].ToString(), cbx.Font, brush, e.Bounds, sf);
- }
- }
+ Utils.Utils.DrawCenterComboBox(sender, e);
}
private void ControlButton_Click(object sender, EventArgs e)
{
- if (!int.TryParse(PortTextBox.Text, out var port))
- {
- return;
- }
+ if (!ushort.TryParse(PortTextBox.Text, out var port)) return;
_server.Remark = RemarkTextBox.Text;
_server.Type = "SS";
@@ -86,9 +61,7 @@ namespace Netch.Forms.Server
_server.Country = null;
if (Global.Settings.Server.IndexOf(_server) == -1)
- {
Global.Settings.Server.Add(_server);
- }
MessageBoxX.Show(i18N.Translate("Saved"));
Close();
diff --git a/Netch/Forms/Server/Shadowsocks.resx b/Netch/ServerEx/Shadowsocks/Form/ShadowsocksForm.resx
similarity index 100%
rename from Netch/Forms/Server/Shadowsocks.resx
rename to Netch/ServerEx/Shadowsocks/Form/ShadowsocksForm.resx
diff --git a/Netch/Models/SSD/Main.cs b/Netch/ServerEx/Shadowsocks/Models/SSD/Main.cs
similarity index 89%
rename from Netch/Models/SSD/Main.cs
rename to Netch/ServerEx/Shadowsocks/Models/SSD/Main.cs
index 61dcce4a..cf9ebe61 100644
--- a/Netch/Models/SSD/Main.cs
+++ b/Netch/ServerEx/Shadowsocks/Models/SSD/Main.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace Netch.Models.SSD
+namespace Netch.ServerEx.Shadowsocks.Models.SSD
{
public class Main
{
@@ -37,6 +37,6 @@ namespace Netch.Models.SSD
///
/// 服务器数组
///
- public List servers;
+ public List servers;
}
-}
+}
\ No newline at end of file
diff --git a/Netch/Models/SSD/Server.cs b/Netch/ServerEx/Shadowsocks/Models/SSD/SSDServer.cs
similarity index 90%
rename from Netch/Models/SSD/Server.cs
rename to Netch/ServerEx/Shadowsocks/Models/SSD/SSDServer.cs
index e7f4ac59..952b8146 100644
--- a/Netch/Models/SSD/Server.cs
+++ b/Netch/ServerEx/Shadowsocks/Models/SSD/SSDServer.cs
@@ -1,6 +1,6 @@
-namespace Netch.Models.SSD
+namespace Netch.ServerEx.Shadowsocks.Models.SSD
{
- public class Server
+ public class SSDServer
{
///
/// 服务器地址
@@ -37,4 +37,4 @@
///
public string remarks;
}
-}
+}
\ No newline at end of file
diff --git a/Netch/Models/SS/ShadowsocksServer.cs b/Netch/ServerEx/Shadowsocks/Models/ShadowsocksConfig.cs
similarity index 80%
rename from Netch/Models/SS/ShadowsocksServer.cs
rename to Netch/ServerEx/Shadowsocks/Models/ShadowsocksConfig.cs
index 00c36322..c8cf9978 100644
--- a/Netch/Models/SS/ShadowsocksServer.cs
+++ b/Netch/ServerEx/Shadowsocks/Models/ShadowsocksConfig.cs
@@ -1,6 +1,6 @@
-namespace Netch.Models.SS
+namespace Netch.ServerEx.Shadowsocks.Models
{
- public class ShadowsocksServer
+ public class ShadowsocksConfig
{
public string server { get; set; }
public int server_port { get; set; }
diff --git a/Netch/ServerEx/Shadowsocks/SSController.cs b/Netch/ServerEx/Shadowsocks/SSController.cs
new file mode 100644
index 00000000..47cea076
--- /dev/null
+++ b/Netch/ServerEx/Shadowsocks/SSController.cs
@@ -0,0 +1,95 @@
+using System.Runtime.InteropServices;
+using System.Text;
+using Netch.Models;
+using Netch.Utils;
+
+namespace Netch.ServerEx.Shadowsocks
+{
+ public class SSController : ServerController
+ {
+ public SSController()
+ {
+ Name = "Shadowsocks";
+ }
+
+ public override bool Start(Server s, Mode mode)
+ {
+ bool DllFlag()
+ {
+ return Global.Settings.BootShadowsocksFromDLL && (mode.Type == 0 || mode.Type == 1 || mode.Type == 2);
+ }
+
+ var server = (Shadowsocks) s;
+ //从DLL启动Shaowsocks
+ if (DllFlag())
+ {
+ State = State.Starting;
+ var client = Encoding.UTF8.GetBytes($"{LocalAddress}:{Socks5LocalPort}");
+ var remote = Encoding.UTF8.GetBytes($"{server.Hostname}:{server.Port}");
+ var passwd = Encoding.UTF8.GetBytes($"{server.Password}");
+ var method = Encoding.UTF8.GetBytes($"{server.EncryptMethod}");
+ if (!ShadowsocksDLL.Info(client, remote, passwd, method))
+ {
+ State = State.Stopped;
+ Logging.Error("DLL SS INFO 设置失败!");
+ return false;
+ }
+
+ Logging.Info("DLL SS INFO 设置成功!");
+
+ if (!ShadowsocksDLL.Start())
+ {
+ State = State.Stopped;
+ Logging.Error("DLL SS 启动失败!");
+ return false;
+ }
+
+ Logging.Info("DLL SS 启动成功!");
+ State = State.Started;
+ return true;
+ }
+
+ #region Argument
+
+ var argument = new StringBuilder();
+ argument.Append(
+ $"-s {server.Hostname} " +
+ $"-p {server.Port} " +
+ $"-b {LocalAddress} " +
+ $"-l {Socks5LocalPort} " +
+ $"-m {server.EncryptMethod} " +
+ $"-k \"{server.Password}\" " +
+ "-u ");
+ if (!string.IsNullOrWhiteSpace(server.Plugin) && !string.IsNullOrWhiteSpace(server.PluginOption))
+ argument.Append($"--plugin {server.Plugin} " +
+ $"--plugin-opts \"{server.PluginOption}\"");
+ if (mode.BypassChina)
+ argument.Append(" --acl default.acl");
+
+ #endregion
+
+ return StartInstanceAuto(argument.ToString());
+ }
+
+ public override void Stop()
+ {
+ if (Instance == null)
+ ShadowsocksDLL.Stop();
+ else
+ StopInstance();
+ }
+
+
+ private class ShadowsocksDLL
+ {
+ [DllImport("shadowsocks-windows-dynamic", CallingConvention = CallingConvention.Cdecl)]
+ public static extern bool Info(byte[] client, byte[] remote, byte[] passwd, byte[] method);
+
+ [DllImport("shadowsocks-windows-dynamic", CallingConvention = CallingConvention.Cdecl)]
+ public static extern bool Start();
+
+ [DllImport("shadowsocks-windows-dynamic", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void Stop();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Netch/ServerEx/Shadowsocks/SSUtil.cs b/Netch/ServerEx/Shadowsocks/SSUtil.cs
new file mode 100644
index 00000000..baabe5ed
--- /dev/null
+++ b/Netch/ServerEx/Shadowsocks/SSUtil.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Web;
+using Netch.Models;
+using Netch.ServerEx.Shadowsocks.Form;
+using Netch.ServerEx.Shadowsocks.Models.SSD;
+using Netch.Utils;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Netch.ServerEx.Shadowsocks
+{
+ public class SSUtil : IServerUtil
+ {
+ public ushort Priority { get; } = 1;
+ public string TypeName { get; } = "SS";
+ public string FullName { get; } = "Shadowsocks";
+
+ public string[] UriScheme { get; } = {"ss","ssd"};
+
+ public Server ParseJObject(JObject j)
+ {
+ return j.ToObject();
+ }
+
+ public void Edit(Server s)
+ {
+ new ShadowsocksForm((Shadowsocks) s).ShowDialog();
+ }
+
+ public void Create()
+ {
+ new ShadowsocksForm().ShowDialog();
+ }
+
+ public string GetShareLink(Server s)
+ {
+ var server = (Shadowsocks) s;
+ // ss://method:password@server:port#Remark
+ return "ss://" + ShareLink.URLSafeBase64Encode($"{server.EncryptMethod}:{server.Password}@{server.Hostname}:{server.Port}") + "#" + HttpUtility.UrlEncode(server.Remark);
+ }
+
+ public ServerController GetController()
+ {
+ return new SSController();
+ }
+
+ public IEnumerable ParseUri(string text)
+ {
+ if (text.StartsWith("ss://"))
+ {
+ return new[] {ParseSsUri(text)};
+ }
+
+ if (text.StartsWith("ssd://"))
+ {
+ return ParseSsdUri(text);
+ }
+
+ return null;
+ }
+
+ public IEnumerable ParseSsdUri(string s)
+ {
+ var json = JsonConvert.DeserializeObject(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) ? string.IsNullOrEmpty(server.plugin_options) ? null : server.plugin_options : json.plugin_options
+ })
+ .Where(CheckServer);
+ }
+
+ public Shadowsocks ParseSsUri(string text)
+ {
+ var data = new Shadowsocks();
+
+ text = text.Replace("/?", "?");
+ try
+ {
+ if (text.Contains("#"))
+ {
+ data.Remark = HttpUtility.UrlDecode(text.Split('#')[1]);
+ text = text.Split('#')[0];
+ }
+
+ if (text.Contains("?"))
+ {
+ var finder = new Regex(@"^(?.+?)\?(.+)$");
+ var match = finder.Match(text);
+
+ if (match.Success)
+ {
+ var plugins = HttpUtility.UrlDecode(HttpUtility.ParseQueryString(new Uri(text).Query).Get("plugin"));
+ if (plugins != null)
+ {
+ var plugin = plugins.Substring(0, plugins.IndexOf(";", StringComparison.Ordinal));
+ var pluginopts = plugins.Substring(plugins.IndexOf(";", StringComparison.Ordinal) + 1);
+ switch (plugin)
+ {
+ case "obfs-local":
+ case "simple-obfs":
+ plugin = "simple-obfs";
+ if (!pluginopts.Contains("obfs="))
+ pluginopts = "obfs=http;obfs-host=" + pluginopts;
+ break;
+ case "simple-obfs-tls":
+ plugin = "simple-obfs";
+ if (!pluginopts.Contains("obfs="))
+ pluginopts = "obfs=tls;obfs-host=" + pluginopts;
+ break;
+ }
+
+ data.Plugin = plugin;
+ data.PluginOption = pluginopts;
+ }
+
+ text = match.Groups["data"].Value;
+ }
+ else
+ {
+ throw new FormatException();
+ }
+ }
+
+ if (text.Contains("@"))
+ {
+ var finder = new Regex(@"^ss://(?.+?)@(?.+):(?\d+)");
+ var parser = new Regex(@"^(?.+?):(?.+)$");
+ var match = finder.Match(text);
+ if (!match.Success) throw new FormatException();
+
+ data.Hostname = match.Groups["server"].Value;
+ data.Port = int.Parse(match.Groups["port"].Value);
+
+ var base64 = ShareLink.URLSafeBase64Decode(match.Groups["base64"].Value);
+ match = parser.Match(base64);
+ if (!match.Success) throw new FormatException();
+
+ data.EncryptMethod = match.Groups["method"].Value;
+ data.Password = match.Groups["password"].Value;
+ }
+ else
+ {
+ var parser = new Regex(@"^((?.+?):(?.+)@(?.+):(?\d+))");
+ var match = parser.Match(ShareLink.URLSafeBase64Decode(text.Replace("ss://", "")));
+ if (!match.Success) throw new FormatException();
+
+ data.Hostname = match.Groups["server"].Value;
+ data.Port = int.Parse(match.Groups["port"].Value);
+ data.EncryptMethod = match.Groups["method"].Value;
+ data.Password = match.Groups["password"].Value;
+ }
+
+ return CheckServer(data) ? data : null;
+ }
+ catch (FormatException)
+ {
+ return null;
+ }
+ }
+
+ public bool CheckServer(Server s)
+ {
+ var server = (Shadowsocks) s;
+ if (!SSGlobal.EncryptMethods.Contains(server.EncryptMethod))
+ {
+ Logging.Error($"不支持的 SS 加密方式:{server.EncryptMethod}");
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Netch/ServerEx/Shadowsocks/Shadowsocks.cs b/Netch/ServerEx/Shadowsocks/Shadowsocks.cs
new file mode 100644
index 00000000..cf105c89
--- /dev/null
+++ b/Netch/ServerEx/Shadowsocks/Shadowsocks.cs
@@ -0,0 +1,62 @@
+using System.Collections.Generic;
+using Netch.Models;
+
+namespace Netch.ServerEx.Shadowsocks
+{
+ public class Shadowsocks : Server
+ {
+ ///
+ /// 加密方式
+ ///
+ public string EncryptMethod { get; set; } = SSGlobal.EncryptMethods[0];
+
+ ///
+ /// 密码
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// 插件
+ ///
+ public string Plugin { get; set; }
+
+ ///
+ /// 插件参数
+ ///
+ public string PluginOption { get; set; }
+
+ public Shadowsocks()
+ {
+ Type = "SS";
+ }
+ }
+
+ public static class SSGlobal
+ {
+ ///
+ /// SS 加密列表
+ ///
+ public static readonly List EncryptMethods = new List
+ {
+ "rc4-md5",
+ "aes-128-gcm",
+ "aes-192-gcm",
+ "aes-256-gcm",
+ "aes-128-cfb",
+ "aes-192-cfb",
+ "aes-256-cfb",
+ "aes-128-ctr",
+ "aes-192-ctr",
+ "aes-256-ctr",
+ "camellia-128-cfb",
+ "camellia-192-cfb",
+ "camellia-256-cfb",
+ "bf-cfb",
+ "chacha20-ietf-poly1305",
+ "xchacha20-ietf-poly1305",
+ "salsa20",
+ "chacha20",
+ "chacha20-ietf"
+ };
+ }
+}
\ No newline at end of file
diff --git a/Netch/Forms/Server/ShadowsocksR.Designer.cs b/Netch/ServerEx/ShadowsocksR/Form/ShadowsocksRForm.Designer.cs
similarity index 99%
rename from Netch/Forms/Server/ShadowsocksR.Designer.cs
rename to Netch/ServerEx/ShadowsocksR/Form/ShadowsocksRForm.Designer.cs
index d90ff9f2..db69bade 100644
--- a/Netch/Forms/Server/ShadowsocksR.Designer.cs
+++ b/Netch/ServerEx/ShadowsocksR/Form/ShadowsocksRForm.Designer.cs
@@ -1,6 +1,6 @@
-namespace Netch.Forms.Server
+namespace Netch.ServerEx.ShadowsocksR.Form
{
- partial class ShadowsocksR
+ partial class ShadowsocksRForm
{
///
/// Required designer variable.
@@ -28,7 +28,7 @@
///
private void InitializeComponent()
{
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ShadowsocksR));
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ShadowsocksRForm));
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
this.OBFSParamLabel = new System.Windows.Forms.Label();
this.OBFSOptionParamTextBox = new System.Windows.Forms.TextBox();
@@ -263,7 +263,7 @@
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
- this.Name = "ShadowsocksR";
+ this.Name = "ShadowsocksRForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "ShadowsocksR";
this.Load += new System.EventHandler(this.ShadowsocksR_Load);
diff --git a/Netch/Forms/Server/ShadowsocksR.cs b/Netch/ServerEx/ShadowsocksR/Form/ShadowsocksRForm.cs
similarity index 56%
rename from Netch/Forms/Server/ShadowsocksR.cs
rename to Netch/ServerEx/ShadowsocksR/Form/ShadowsocksRForm.cs
index 4b6ce9df..bcf623a0 100644
--- a/Netch/Forms/Server/ShadowsocksR.cs
+++ b/Netch/ServerEx/ShadowsocksR/Form/ShadowsocksRForm.cs
@@ -1,19 +1,18 @@
using System;
-using System.Drawing;
using System.Windows.Forms;
using Netch.Utils;
-namespace Netch.Forms.Server
+namespace Netch.ServerEx.ShadowsocksR.Form
{
- public partial class ShadowsocksR : Form
+ public partial class ShadowsocksRForm : System.Windows.Forms.Form
{
- private readonly Models.Server _server;
+ private readonly ShadowsocksR _server;
- public ShadowsocksR(Models.Server server = default)
+ public ShadowsocksRForm(Models.Server server = default)
{
InitializeComponent();
- _server = server ?? new Models.Server {EncryptMethod = Global.EncryptMethods.SSR[0]};
+ _server = (ShadowsocksR) (server ?? new ShadowsocksR());
}
private void ShadowsocksR_Load(object sender, EventArgs e)
@@ -31,10 +30,10 @@ namespace Netch.Forms.Server
OBFSParamLabel.Text = i18N.Translate(OBFSParamLabel.Text);
ControlButton.Text = i18N.Translate(ControlButton.Text);
- EncryptMethodComboBox.Items.AddRange(Global.EncryptMethods.SSR.ToArray());
+ EncryptMethodComboBox.Items.AddRange(SSRGlobal.EncryptMethods.ToArray());
- ProtocolComboBox.Items.AddRange(Global.Protocols.ToArray());
- OBFSComboBox.Items.AddRange(Global.OBFSs.ToArray());
+ ProtocolComboBox.Items.AddRange(SSRGlobal.Protocols.ToArray());
+ OBFSComboBox.Items.AddRange(SSRGlobal.OBFSs.ToArray());
#endregion
@@ -42,45 +41,21 @@ namespace Netch.Forms.Server
AddressTextBox.Text = _server.Hostname;
PortTextBox.Text = _server.Port.ToString();
PasswordTextBox.Text = _server.Password;
- EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.SSR.IndexOf(_server.EncryptMethod);
- ProtocolComboBox.SelectedIndex = Global.Protocols.IndexOf(_server.Protocol);
+ EncryptMethodComboBox.SelectedIndex = SSRGlobal.EncryptMethods.IndexOf(_server.EncryptMethod);
+ ProtocolComboBox.SelectedIndex = SSRGlobal.Protocols.IndexOf(_server.Protocol);
ProtocolParamTextBox.Text = _server.ProtocolParam;
- OBFSComboBox.SelectedIndex = Global.OBFSs.IndexOf(_server.OBFS);
+ OBFSComboBox.SelectedIndex = SSRGlobal.OBFSs.IndexOf(_server.OBFS);
OBFSOptionParamTextBox.Text = _server.OBFSParam;
}
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
- if (sender is ComboBox cbx)
- {
- e.DrawBackground();
-
- if (e.Index >= 0)
- {
- var sf = new StringFormat
- {
- LineAlignment = StringAlignment.Center,
- Alignment = StringAlignment.Center
- };
-
- var brush = new SolidBrush(cbx.ForeColor);
-
- if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
- {
- brush = SystemBrushes.HighlightText as SolidBrush;
- }
-
- e.Graphics.DrawString(cbx.Items[e.Index].ToString(), cbx.Font, brush, e.Bounds, sf);
- }
- }
+ Utils.Utils.DrawCenterComboBox(sender, e);
}
private void ControlButton_Click(object sender, EventArgs e)
{
- if (!int.TryParse(PortTextBox.Text, out var port))
- {
- return;
- }
+ if (!ushort.TryParse(PortTextBox.Text, out var port)) return;
_server.Remark = RemarkTextBox.Text;
_server.Type = "SSR";
@@ -95,9 +70,7 @@ namespace Netch.Forms.Server
_server.Country = null;
if (Global.Settings.Server.IndexOf(_server) == -1)
- {
Global.Settings.Server.Add(_server);
- }
MessageBoxX.Show(i18N.Translate("Saved"));
Close();
diff --git a/Netch/Forms/Server/ShadowsocksR.resx b/Netch/ServerEx/ShadowsocksR/Form/ShadowsocksRForm.resx
similarity index 100%
rename from Netch/Forms/Server/ShadowsocksR.resx
rename to Netch/ServerEx/ShadowsocksR/Form/ShadowsocksRForm.resx
diff --git a/Netch/Controllers/EncryptedProxy/SSRController.cs b/Netch/ServerEx/ShadowsocksR/SSRController.cs
similarity index 80%
rename from Netch/Controllers/EncryptedProxy/SSRController.cs
rename to Netch/ServerEx/ShadowsocksR/SSRController.cs
index 0fb018ec..ce111a0c 100644
--- a/Netch/Controllers/EncryptedProxy/SSRController.cs
+++ b/Netch/ServerEx/ShadowsocksR/SSRController.cs
@@ -1,20 +1,21 @@
-using System.Text;
+using System.Text;
using Netch.Models;
-namespace Netch.Controllers
+namespace Netch.ServerEx.ShadowsocksR
{
- public class SSRController : EncryptedProxy
+ public class SSRController : ServerController
{
public SSRController()
{
Name = "ShadowsocksR";
MainFile = "ShadowsocksR.exe";
- StartedKeywords.Add("listening at");
- StoppedKeywords.AddRange(new[] {"Invalid config path", "usage"});
}
- public override bool Start(Server server, Mode mode)
+
+ public override bool Start(Server s, Mode mode)
{
+ var server = (ShadowsocksR) s;
+
#region Argument
var argument = new StringBuilder();
diff --git a/Netch/ServerEx/ShadowsocksR/SSRUtil.cs b/Netch/ServerEx/ShadowsocksR/SSRUtil.cs
new file mode 100644
index 00000000..9ca60d18
--- /dev/null
+++ b/Netch/ServerEx/ShadowsocksR/SSRUtil.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using Netch.Models;
+using Netch.ServerEx.ShadowsocksR.Form;
+using Netch.Utils;
+using Newtonsoft.Json.Linq;
+
+namespace Netch.ServerEx.ShadowsocksR
+{
+ public class SSRUtil : IServerUtil
+ {
+ public ushort Priority { get; } = 1;
+ public string TypeName { get; } = "SSR";
+ public string FullName { get; } = "ShadowsocksR";
+
+ public string[] UriScheme { get; } = {"ssr"};
+
+ public Server ParseJObject(JObject j)
+ {
+ return j.ToObject();
+ }
+
+
+ public void Edit(Server s)
+ {
+ new ShadowsocksRForm(s).ShowDialog();
+ }
+
+ public void Create()
+ {
+ new ShadowsocksRForm().ShowDialog();
+ }
+
+ public string GetShareLink(Server s)
+ {
+ var server = (ShadowsocksR) s;
+
+ // https://github.com/shadowsocksr-backup/shadowsocks-rss/wiki/SSR-QRcode-scheme
+ // ssr://base64(host:port:protocol:method:obfs:base64pass/?obfsparam=base64param&protoparam=base64param&remarks=base64remarks&group=base64group&udpport=0&uot=0)
+ var paraStr = $"/?obfsparam={ShareLink.URLSafeBase64Encode(server.OBFSParam)}&protoparam={ShareLink.URLSafeBase64Encode(server.ProtocolParam)}&remarks={ShareLink.URLSafeBase64Encode(server.Remark)}";
+ return "ssr://" + ShareLink.URLSafeBase64Encode($"{server.Hostname}:{server.Port}:{server.Protocol}:{server.EncryptMethod}:{server.OBFS}:{ShareLink.URLSafeBase64Encode(server.Password)}{paraStr}");
+ }
+
+ public ServerController GetController()
+ {
+ return new SSRController();
+ }
+
+ ///
+ /// SSR链接解析器
+ /// Copy From https://github.com/HMBSbige/ShadowsocksR-Windows/blob/d9dc8d032a6e04c14b9dc6c8f673c9cc5aa9f607/shadowsocks-csharp/Model/Server.cs#L428
+ /// Thx :D
+ ///
+ ///
+ ///
+ public IEnumerable ParseUri(string text)
+ {
+ // ssr://host:port:protocol:method:obfs:base64pass/?obfsparam=base64&remarks=base64&group=base64&udpport=0&uot=1
+ var ssr = Regex.Match(text, "ssr://([A-Za-z0-9_-]+)", RegexOptions.IgnoreCase);
+ if (!ssr.Success)
+ throw new FormatException();
+
+ var data = ShareLink.URLSafeBase64Decode(ssr.Groups[1].Value);
+ var paramsDict = new Dictionary();
+
+ var paramStartPos = data.IndexOf("?", StringComparison.Ordinal);
+ if (paramStartPos > 0)
+ {
+ paramsDict = ShareLink.ParseParam(data.Substring(paramStartPos + 1));
+ data = data.Substring(0, paramStartPos);
+ }
+
+ if (data.IndexOf("/", StringComparison.Ordinal) >= 0) data = data.Substring(0, data.LastIndexOf("/", StringComparison.Ordinal));
+
+ var urlFinder = new Regex("^(.+):([^:]+):([^:]*):([^:]+):([^:]*):([^:]+)");
+ var match = urlFinder.Match(data);
+
+ if (match == null || !match.Success)
+ throw new FormatException();
+
+ var serverAddr = match.Groups[1].Value;
+ var serverPort = ushort.Parse(match.Groups[2].Value);
+ var protocol = match.Groups[3].Value.Length == 0 ? "origin" : match.Groups[3].Value;
+ protocol = protocol.Replace("_compatible", "");
+ var method = match.Groups[4].Value;
+ var obfs = match.Groups[5].Value.Length == 0 ? "plain" : match.Groups[5].Value;
+ obfs = obfs.Replace("_compatible", "");
+ var password = ShareLink.URLSafeBase64Decode(match.Groups[6].Value);
+ var protocolParam = "";
+ var obfsParam = "";
+ var remarks = "";
+
+ if (paramsDict.ContainsKey("protoparam")) protocolParam = ShareLink.URLSafeBase64Decode(paramsDict["protoparam"]);
+
+ if (paramsDict.ContainsKey("obfsparam")) obfsParam = ShareLink.URLSafeBase64Decode(paramsDict["obfsparam"]);
+
+ if (paramsDict.ContainsKey("remarks")) remarks = ShareLink.URLSafeBase64Decode(paramsDict["remarks"]);
+
+ var group = paramsDict.ContainsKey("group") ? ShareLink.URLSafeBase64Decode(paramsDict["group"]) : string.Empty;
+
+ return new[]
+ {
+ new ShadowsocksR
+ {
+ Hostname = serverAddr,
+ Port = serverPort,
+ Protocol = protocol,
+ EncryptMethod = method,
+ OBFS = obfs,
+ Password = password,
+ ProtocolParam = protocolParam,
+ OBFSParam = obfsParam,
+ Remark = remarks,
+ Group = group
+ }
+ };
+ }
+
+ public bool CheckServer(Server s)
+ {
+ var server = (ShadowsocksR) s;
+ if (!SSRGlobal.EncryptMethods.Contains(server.EncryptMethod))
+ {
+ Logging.Error($"不支持的 SSR 加密方式:{server.EncryptMethod}");
+ return false;
+ }
+
+ if (!SSRGlobal.Protocols.Contains(server.Protocol))
+ {
+ Logging.Error($"不支持的 SSR 协议:{server.Protocol}");
+ return false;
+ }
+
+ if (!SSRGlobal.OBFSs.Contains(server.OBFS))
+ {
+ Logging.Error($"不支持的 SSR 混淆:{server.OBFS}");
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Netch/ServerEx/ShadowsocksR/ShadowsocksR.cs b/Netch/ServerEx/ShadowsocksR/ShadowsocksR.cs
new file mode 100644
index 00000000..a807cd14
--- /dev/null
+++ b/Netch/ServerEx/ShadowsocksR/ShadowsocksR.cs
@@ -0,0 +1,100 @@
+using System.Collections.Generic;
+using Netch.Models;
+
+namespace Netch.ServerEx.ShadowsocksR
+{
+ public class ShadowsocksR : Server
+ {
+ ///
+ /// 加密方式
+ ///
+ public string EncryptMethod { get; set; } = SSRGlobal.EncryptMethods[0];
+
+ ///
+ /// 混淆
+ ///
+ public string OBFS { get; set; } = SSRGlobal.OBFSs[0];
+
+ ///
+ /// 混淆参数
+ ///
+ public string OBFSParam { get; set; }
+
+ ///
+ /// 密码
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// 协议
+ ///
+ public string Protocol { get; set; } = SSRGlobal.Protocols[0];
+
+ ///
+ /// 协议参数
+ ///
+ public string ProtocolParam { get; set; }
+
+ public ShadowsocksR()
+ {
+ Type = "SSR";
+ }
+ }
+
+ public class SSRGlobal
+ {
+ ///
+ /// SSR 协议列表
+ ///
+ public static readonly List Protocols = new List
+ {
+ "origin",
+ "verify_deflate",
+ "auth_sha1_v4",
+ "auth_aes128_md5",
+ "auth_aes128_sha1",
+ "auth_chain_a"
+ };
+
+ ///
+ /// SSR 混淆列表
+ ///
+ public static readonly List OBFSs = new List
+ {
+ "plain",
+ "http_simple",
+ "http_post",
+ "tls1.2_ticket_auth"
+ };
+
+ ///
+ /// SS/SSR 加密方式
+ ///
+ public static readonly List EncryptMethods = new List
+ {
+ "none",
+ "table",
+ "rc4",
+ "rc4-md5",
+ "rc4-md5-6",
+ "aes-128-cfb",
+ "aes-192-cfb",
+ "aes-256-cfb",
+ "aes-128-ctr",
+ "aes-192-ctr",
+ "aes-256-ctr",
+ "bf-cfb",
+ "camellia-128-cfb",
+ "camellia-192-cfb",
+ "camellia-256-cfb",
+ "cast5-cfb",
+ "des-cfb",
+ "idea-cfb",
+ "rc2-cfb",
+ "seed-cfb",
+ "salsa20",
+ "chacha20",
+ "chacha20-ietf"
+ };
+ }
+}
\ No newline at end of file
diff --git a/Netch/Forms/Server/Socks5.Designer.cs b/Netch/ServerEx/Socks5/Form/Socks5Form.Designer.cs
similarity index 98%
rename from Netch/Forms/Server/Socks5.Designer.cs
rename to Netch/ServerEx/Socks5/Form/Socks5Form.Designer.cs
index c6f1006f..6e2404fd 100644
--- a/Netch/Forms/Server/Socks5.Designer.cs
+++ b/Netch/ServerEx/Socks5/Form/Socks5Form.Designer.cs
@@ -1,6 +1,6 @@
-namespace Netch.Forms.Server
+namespace Netch.ServerEx.Socks5.Form
{
- partial class Socks5
+ partial class Socks5Form
{
///
/// Required designer variable.
@@ -28,7 +28,7 @@
///
private void InitializeComponent()
{
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Socks5));
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Socks5Form));
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
this.PasswordLabel = new System.Windows.Forms.Label();
this.UsernameTextBox = new System.Windows.Forms.TextBox();
@@ -170,7 +170,7 @@
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
- this.Name = "Socks5";
+ this.Name = "Socks5Form";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Socks5";
this.Load += new System.EventHandler(this.Shadowsocks_Load);
diff --git a/Netch/Forms/Server/Socks5.cs b/Netch/ServerEx/Socks5/Form/Socks5Form.cs
similarity index 54%
rename from Netch/Forms/Server/Socks5.cs
rename to Netch/ServerEx/Socks5/Form/Socks5Form.cs
index c0db97e8..35c8d9c1 100644
--- a/Netch/Forms/Server/Socks5.cs
+++ b/Netch/ServerEx/Socks5/Form/Socks5Form.cs
@@ -1,19 +1,17 @@
using System;
-using System.Drawing;
-using System.Windows.Forms;
using Netch.Utils;
-namespace Netch.Forms.Server
+namespace Netch.ServerEx.Socks5.Form
{
- public partial class Socks5 : Form
+ public partial class Socks5Form : System.Windows.Forms.Form
{
- private readonly Models.Server _server;
+ private readonly Socks5 _server;
- public Socks5(Models.Server server = default)
+ public Socks5Form(Models.Server server = default)
{
InitializeComponent();
- _server = server ?? new Models.Server();
+ _server = (Socks5) (server ?? new Socks5());
}
private void Shadowsocks_Load(object sender, EventArgs e)
@@ -36,38 +34,9 @@ namespace Netch.Forms.Server
PasswordTextBox.Text = _server.Password;
}
- private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
- {
- if (sender is ComboBox cbx)
- {
- e.DrawBackground();
-
- if (e.Index >= 0)
- {
- var sf = new StringFormat
- {
- LineAlignment = StringAlignment.Center,
- Alignment = StringAlignment.Center
- };
-
- var brush = new SolidBrush(cbx.ForeColor);
-
- if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
- {
- brush = SystemBrushes.HighlightText as SolidBrush;
- }
-
- e.Graphics.DrawString(cbx.Items[e.Index].ToString(), cbx.Font, brush, e.Bounds, sf);
- }
- }
- }
-
private void ControlButton_Click(object sender, EventArgs e)
{
- if (!int.TryParse(PortTextBox.Text, out var port))
- {
- return;
- }
+ if (!ushort.TryParse(PortTextBox.Text, out var port)) return;
_server.Remark = RemarkTextBox.Text;
_server.Type = "Socks5";
@@ -78,9 +47,7 @@ namespace Netch.Forms.Server
_server.Country = null;
if (Global.Settings.Server.IndexOf(_server) == -1)
- {
Global.Settings.Server.Add(_server);
- }
MessageBoxX.Show(i18N.Translate("Saved"));
Close();
diff --git a/Netch/Forms/Server/Socks5.resx b/Netch/ServerEx/Socks5/Form/Socks5Form.resx
similarity index 100%
rename from Netch/Forms/Server/Socks5.resx
rename to Netch/ServerEx/Socks5/Form/Socks5Form.resx
diff --git a/Netch/ServerEx/Socks5/S5Util.cs b/Netch/ServerEx/Socks5/S5Util.cs
new file mode 100644
index 00000000..d5fc42af
--- /dev/null
+++ b/Netch/ServerEx/Socks5/S5Util.cs
@@ -0,0 +1,81 @@
+using System.Collections.Generic;
+using System.Linq;
+using Netch.Models;
+using Netch.ServerEx.Socks5.Form;
+using Newtonsoft.Json.Linq;
+
+namespace Netch.ServerEx.Socks5
+{
+ public class S5Util : IServerUtil
+ {
+ public ushort Priority { get; } = 0;
+ public string TypeName { get; } = "Socks5";
+ public string FullName { get; } = "Socks5";
+
+ public string[] UriScheme { get; } = { };
+
+ public Server ParseJObject(JObject j)
+ {
+ return j.ToObject();
+ }
+
+ public void Edit(Server s)
+ {
+ new Socks5Form(s).ShowDialog();
+ }
+
+ public void Create()
+ {
+ new Socks5Form().ShowDialog();
+ }
+
+ public string GetShareLink(Server server)
+ {
+ // https://t.me/socks?server=1.1.1.1&port=443
+ return $"https://t.me/socks?server={server.Hostname}&port={server.Port}";
+ }
+
+ public ServerController GetController()
+ {
+ return null;
+ }
+
+ public IEnumerable ParseUri(string text)
+ {
+ var dict = text
+ .Replace("tg://socks?", "")
+ .Replace("https://t.me/socks?", "")
+ .Split('&')
+ .Select(str => str.Split('='))
+ .ToDictionary(splited => splited[0], splited => splited[1]);
+
+ if (!dict.ContainsKey("server") || !dict.ContainsKey("port"))
+ {
+ return null;
+ }
+
+ var data = new Socks5
+ {
+ Hostname = dict["server"],
+ Port = int.Parse(dict["port"])
+ };
+
+ if (dict.ContainsKey("user") && !string.IsNullOrWhiteSpace(dict["user"]))
+ {
+ data.Username = dict["user"];
+ }
+
+ if (dict.ContainsKey("pass") && !string.IsNullOrWhiteSpace(dict["pass"]))
+ {
+ data.Password = dict["pass"];
+ }
+
+ return new[] {data};
+ }
+
+ public bool CheckServer(Server s)
+ {
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Netch/ServerEx/Socks5/Socks5.cs b/Netch/ServerEx/Socks5/Socks5.cs
new file mode 100644
index 00000000..d500e76b
--- /dev/null
+++ b/Netch/ServerEx/Socks5/Socks5.cs
@@ -0,0 +1,22 @@
+using Netch.Models;
+
+namespace Netch.ServerEx.Socks5
+{
+ public class Socks5 : Server
+ {
+ ///
+ /// 密码
+ ///
+ public string Password;
+
+ ///
+ /// 账号
+ ///
+ public string Username;
+
+ public Socks5()
+ {
+ Type = "Socks5";
+ }
+ }
+}
\ No newline at end of file
diff --git a/Netch/Forms/Server/Trojan.Designer.cs b/Netch/ServerEx/Trojan/Form/TrojanForm.Designer.cs
similarity index 97%
rename from Netch/Forms/Server/Trojan.Designer.cs
rename to Netch/ServerEx/Trojan/Form/TrojanForm.Designer.cs
index 317524c4..e85c25fa 100644
--- a/Netch/Forms/Server/Trojan.Designer.cs
+++ b/Netch/ServerEx/Trojan/Form/TrojanForm.Designer.cs
@@ -1,6 +1,6 @@
-namespace Netch.Forms.Server
+namespace Netch.ServerEx.Trojan.Form
{
- partial class Trojan
+ partial class TrojanForm
{
///
/// Required designer variable.
@@ -28,7 +28,7 @@
///
private void InitializeComponent()
{
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Trojan));
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TrojanForm));
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
this.PasswordTextBox = new System.Windows.Forms.TextBox();
this.PasswordLabel = new System.Windows.Forms.Label();
@@ -149,10 +149,10 @@
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
- this.Name = "Trojan";
+ this.Name = "TrojanForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Trojan";
- this.Load += new System.EventHandler(this.Trojan_Load);
+ this.Load += new System.EventHandler(this.TrojanForm_Load);
this.ConfigurationGroupBox.ResumeLayout(false);
this.ConfigurationGroupBox.PerformLayout();
this.ResumeLayout(false);
diff --git a/Netch/Forms/Server/Trojan.cs b/Netch/ServerEx/Trojan/Form/TrojanForm.cs
similarity index 52%
rename from Netch/Forms/Server/Trojan.cs
rename to Netch/ServerEx/Trojan/Form/TrojanForm.cs
index df0ea1bc..f3d2489c 100644
--- a/Netch/Forms/Server/Trojan.cs
+++ b/Netch/ServerEx/Trojan/Form/TrojanForm.cs
@@ -1,22 +1,22 @@
using System;
-using System.Drawing;
using System.Windows.Forms;
+using Netch.Models;
using Netch.Utils;
-namespace Netch.Forms.Server
+namespace Netch.ServerEx.Trojan.Form
{
- public partial class Trojan : Form
+ public partial class TrojanForm : System.Windows.Forms.Form
{
- private readonly Models.Server _server;
+ private readonly Trojan _server;
- public Trojan(Models.Server server = default)
+ public TrojanForm(Server server = default)
{
InitializeComponent();
- _server = server ?? new Models.Server();
+ _server = (Trojan) (server ?? new Trojan());
}
- private void Trojan_Load(object sender, EventArgs e)
+ private void TrojanForm_Load(object sender, EventArgs e)
{
ConfigurationGroupBox.Text = i18N.Translate(ConfigurationGroupBox.Text);
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
@@ -32,37 +32,12 @@ namespace Netch.Forms.Server
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
- if (sender is ComboBox cbx)
- {
- e.DrawBackground();
-
- if (e.Index >= 0)
- {
- var sf = new StringFormat
- {
- LineAlignment = StringAlignment.Center,
- Alignment = StringAlignment.Center
- };
-
- var brush = new SolidBrush(cbx.ForeColor);
-
- if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
- {
- brush = SystemBrushes.HighlightText as SolidBrush;
- }
-
- e.Graphics.DrawString(cbx.Items[e.Index].ToString(), cbx.Font, brush, e.Bounds, sf);
- }
- }
+ Utils.Utils.DrawCenterComboBox(sender, e);
}
private void ControlButton_Click(object sender, EventArgs e)
{
- if (!int.TryParse(PortTextBox.Text, out var port))
- {
- return;
- }
-
+ if (!ushort.TryParse(PortTextBox.Text, out var port)) return;
_server.Remark = RemarkTextBox.Text;
_server.Type = "Trojan";
@@ -72,9 +47,7 @@ namespace Netch.Forms.Server
_server.Country = null;
if (Global.Settings.Server.IndexOf(_server) == -1)
- {
Global.Settings.Server.Add(_server);
- }
MessageBoxX.Show(i18N.Translate("Saved"));
Close();
diff --git a/Netch/Forms/Server/Trojan.resx b/Netch/ServerEx/Trojan/Form/TrojanForm.resx
similarity index 100%
rename from Netch/Forms/Server/Trojan.resx
rename to Netch/ServerEx/Trojan/Form/TrojanForm.resx
diff --git a/Netch/Models/Trojan.cs b/Netch/ServerEx/Trojan/Models/TrojanConfig.cs
similarity index 96%
rename from Netch/Models/Trojan.cs
rename to Netch/ServerEx/Trojan/Models/TrojanConfig.cs
index 3301a16e..57ff417c 100644
--- a/Netch/Models/Trojan.cs
+++ b/Netch/ServerEx/Trojan/Models/TrojanConfig.cs
@@ -1,8 +1,8 @@
using System.Collections.Generic;
-namespace Netch.Models
+namespace Netch.ServerEx.Trojan.Models
{
- public class Trojan
+ public class TrojanConfig
{
///
/// 启动类型
@@ -51,11 +51,13 @@ namespace Netch.Models
public string cipher = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA";
public string cipher_tls13 = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384";
public string sni = string.Empty;
+
public List alpn = new List
{
"h2",
"http/1.1"
};
+
public bool reuse_session = true;
public bool session_ticket = true;
public string curves = "";
@@ -69,4 +71,4 @@ namespace Netch.Models
public bool fast_open = true;
public int fast_open_qlen = 20;
}
-}
+}
\ No newline at end of file
diff --git a/Netch/ServerEx/Trojan/Trojan.cs b/Netch/ServerEx/Trojan/Trojan.cs
new file mode 100644
index 00000000..17474ccc
--- /dev/null
+++ b/Netch/ServerEx/Trojan/Trojan.cs
@@ -0,0 +1,22 @@
+using Netch.Models;
+
+namespace Netch.ServerEx.Trojan
+{
+ public class Trojan : Server
+ {
+ public Trojan()
+ {
+ Type = "Trojan";
+ }
+
+ ///
+ /// 密码
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// 伪装域名
+ ///
+ public string Host { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Netch/Controllers/EncryptedProxy/TrojanController.cs b/Netch/ServerEx/Trojan/TrojanController.cs
similarity index 78%
rename from Netch/Controllers/EncryptedProxy/TrojanController.cs
rename to Netch/ServerEx/Trojan/TrojanController.cs
index e9bdc229..e1c77fbe 100644
--- a/Netch/Controllers/EncryptedProxy/TrojanController.cs
+++ b/Netch/ServerEx/Trojan/TrojanController.cs
@@ -1,11 +1,12 @@
using System.Collections.Generic;
using System.IO;
using Netch.Models;
+using Netch.ServerEx.Trojan.Models;
using Newtonsoft.Json;
-namespace Netch.Controllers
+namespace Netch.ServerEx.Trojan
{
- public class TrojanController : EncryptedProxy
+ public class TrojanController : ServerController
{
public TrojanController()
{
@@ -15,9 +16,10 @@ namespace Netch.Controllers
StoppedKeywords.Add("exiting");
}
- public override bool Start(Server server, Mode mode)
+ public override bool Start(Server s, Mode mode)
{
- File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new Trojan
+ var server = (Trojan) s;
+ File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new TrojanConfig
{
local_addr = LocalAddress,
local_port = Socks5LocalPort,
diff --git a/Netch/ServerEx/Trojan/TrojanUtil.cs b/Netch/ServerEx/Trojan/TrojanUtil.cs
new file mode 100644
index 00000000..ccc3189e
--- /dev/null
+++ b/Netch/ServerEx/Trojan/TrojanUtil.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Web;
+using Netch.Models;
+using Netch.ServerEx.Trojan.Form;
+using Newtonsoft.Json.Linq;
+
+namespace Netch.ServerEx.Trojan
+{
+ public class TrojanUtil : IServerUtil
+ {
+ public ushort Priority { get; } = 2;
+ public string TypeName { get; } = "Trojan";
+ public string FullName { get; } = "Trojan";
+
+ public string[] UriScheme { get; } = {"trojan"};
+
+ public Server ParseJObject(JObject j)
+ {
+ return j.ToObject();
+ }
+
+ public void Edit(Server s)
+ {
+ new TrojanForm(s).ShowDialog();
+ }
+
+ public void Create()
+ {
+ new TrojanForm().ShowDialog();
+ }
+
+ public string GetShareLink(Server server)
+ {
+ // TODO
+ return "";
+ }
+
+ public ServerController GetController()
+ {
+ return new TrojanController();
+ }
+
+ public IEnumerable ParseUri(string text)
+ {
+ var data = new Trojan();
+
+ text = text.Replace("/?", "?");
+ try
+ {
+ if (text.Contains("#"))
+ {
+ data.Remark = HttpUtility.UrlDecode(text.Split('#')[1]);
+ text = text.Split('#')[0];
+ }
+
+ if (text.Contains("?"))
+ {
+ var reg = new Regex(@"^(?.+?)\?(.+)$");
+ var regmatch = reg.Match(text);
+
+ if (regmatch.Success)
+ {
+ var peer = HttpUtility.UrlDecode(HttpUtility.ParseQueryString(new Uri(text).Query).Get("peer"));
+
+ if (peer != null)
+ data.Host = peer;
+
+ text = regmatch.Groups["data"].Value;
+ }
+ else
+ {
+ throw new FormatException();
+ }
+ }
+
+ var finder = new Regex(@"^trojan://(?.+?)@(?.+):(?\d+)");
+ var match = finder.Match(text);
+ if (!match.Success)
+ {
+ throw new FormatException();
+ }
+
+ data.Password = match.Groups["psk"].Value;
+ data.Hostname = match.Groups["server"].Value;
+ data.Port = int.Parse(match.Groups["port"].Value);
+
+ return new[] {data};
+ }
+ catch (FormatException)
+ {
+ return null;
+ }
+ }
+
+ public bool CheckServer(Server s)
+ {
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Netch/Forms/Server/Vmess.Designer.cs b/Netch/ServerEx/VMess/Form/VMessForm.Designer.cs
similarity index 99%
rename from Netch/Forms/Server/Vmess.Designer.cs
rename to Netch/ServerEx/VMess/Form/VMessForm.Designer.cs
index 5bcb0ec0..a4ba81ec 100644
--- a/Netch/Forms/Server/Vmess.Designer.cs
+++ b/Netch/ServerEx/VMess/Form/VMessForm.Designer.cs
@@ -1,6 +1,6 @@
-namespace Netch.Forms.Server
+namespace Netch.ServerEx.VMess.Form
{
- partial class VMess
+ partial class VMessForm
{
///
/// Required designer variable.
@@ -28,7 +28,7 @@
///
private void InitializeComponent()
{
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(VMess));
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(VMessForm));
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
this.UseMuxCheckBox = new System.Windows.Forms.CheckBox();
this.TLSSecureCheckBox = new System.Windows.Forms.CheckBox();
@@ -382,7 +382,7 @@
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6);
this.MaximizeBox = false;
- this.Name = "VMess";
+ this.Name = "VMessForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "VMess";
this.Load += new System.EventHandler(this.VMess_Load);
diff --git a/Netch/Forms/Server/Vmess.cs b/Netch/ServerEx/VMess/Form/VMessForm.cs
similarity index 60%
rename from Netch/Forms/Server/Vmess.cs
rename to Netch/ServerEx/VMess/Form/VMessForm.cs
index a26c50e2..a28ed9ef 100644
--- a/Netch/Forms/Server/Vmess.cs
+++ b/Netch/ServerEx/VMess/Form/VMessForm.cs
@@ -1,45 +1,24 @@
using System;
-using System.Drawing;
using System.Windows.Forms;
+using Netch.Models;
using Netch.Utils;
-namespace Netch.Forms.Server
+namespace Netch.ServerEx.VMess.Form
{
- public partial class VMess : Form
+ public partial class VMessForm : System.Windows.Forms.Form
{
- private static Models.Server _server;
+ private static VMess _server;
- public VMess(Models.Server server = default)
+ public VMessForm(Server server = default)
{
InitializeComponent();
- _server = server ?? new Models.Server {EncryptMethod = Global.EncryptMethods.VMess[0]};
+ _server = (VMess) server ?? new VMess();
}
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
- if (sender is ComboBox cbx)
- {
- e.DrawBackground();
-
- if (e.Index >= 0)
- {
- var sf = new StringFormat
- {
- LineAlignment = StringAlignment.Center,
- Alignment = StringAlignment.Center
- };
-
- var brush = new SolidBrush(cbx.ForeColor);
-
- if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
- {
- brush = SystemBrushes.HighlightText as SolidBrush;
- }
-
- e.Graphics.DrawString(cbx.Items[e.Index].ToString(), cbx.Font, brush, e.Bounds, sf);
- }
- }
+ Utils.Utils.DrawCenterComboBox(sender, e);
}
private void VMess_Load(object sender, EventArgs e)
@@ -62,10 +41,10 @@ namespace Netch.Forms.Server
UseMuxCheckBox.Text = i18N.Translate(UseMuxCheckBox.Text);
ControlButton.Text = i18N.Translate(ControlButton.Text);
- EncryptMethodComboBox.Items.AddRange(Global.EncryptMethods.VMess.ToArray());
- TransferProtocolComboBox.Items.AddRange(Global.TransferProtocols.ToArray());
- FakeTypeComboBox.Items.AddRange(Global.FakeTypes.ToArray());
- QUICSecurityComboBox.Items.AddRange(Global.EncryptMethods.VMessQUIC.ToArray());
+ EncryptMethodComboBox.Items.AddRange(VMessGlobal.EncryptMethods.ToArray());
+ TransferProtocolComboBox.Items.AddRange(VMessGlobal.TransferProtocols.ToArray());
+ FakeTypeComboBox.Items.AddRange(VMessGlobal.FakeTypes.ToArray());
+ QUICSecurityComboBox.Items.AddRange(VMessGlobal.QUIC.ToArray());
#endregion
@@ -74,12 +53,12 @@ namespace Netch.Forms.Server
PortTextBox.Text = _server.Port.ToString();
UserIDTextBox.Text = _server.UserID;
AlterIDTextBox.Text = _server.AlterID.ToString();
- EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.VMess.IndexOf(_server.EncryptMethod);
- TransferProtocolComboBox.SelectedIndex = Global.TransferProtocols.IndexOf(_server.TransferProtocol);
- FakeTypeComboBox.SelectedIndex = Global.FakeTypes.IndexOf(_server.FakeType);
+ EncryptMethodComboBox.SelectedIndex = VMessGlobal.EncryptMethods.IndexOf(_server.EncryptMethod);
+ TransferProtocolComboBox.SelectedIndex = VMessGlobal.TransferProtocols.IndexOf(_server.TransferProtocol);
+ FakeTypeComboBox.SelectedIndex = VMessGlobal.FakeTypes.IndexOf(_server.FakeType);
HostTextBox.Text = _server.Host;
PathTextBox.Text = _server.Path;
- QUICSecurityComboBox.SelectedIndex = Global.EncryptMethods.VMessQUIC.IndexOf(_server.QUICSecure);
+ QUICSecurityComboBox.SelectedIndex = VMessGlobal.QUIC.IndexOf(_server.QUICSecure);
QUICSecretTextBox.Text = _server.QUICSecret;
TLSSecureCheckBox.Checked = _server.TLSSecure;
UseMuxCheckBox.Checked = _server.UseMux;
@@ -87,15 +66,9 @@ namespace Netch.Forms.Server
private void ControlButton_Click(object sender, EventArgs e)
{
- if (!int.TryParse(PortTextBox.Text, out var port))
- {
- return;
- }
+ if (!ushort.TryParse(PortTextBox.Text, out var port)) return;
- if (!int.TryParse(AlterIDTextBox.Text, out var alterId))
- {
- return;
- }
+ if (!int.TryParse(AlterIDTextBox.Text, out var alterId)) return;
_server.Remark = RemarkTextBox.Text;
_server.Type = "VMess";
@@ -115,9 +88,7 @@ namespace Netch.Forms.Server
_server.Country = null;
if (Global.Settings.Server.IndexOf(_server) == -1)
- {
Global.Settings.Server.Add(_server);
- }
MessageBoxX.Show(i18N.Translate("Saved"));
Close();
diff --git a/Netch/Forms/Server/Vmess.resx b/Netch/ServerEx/VMess/Form/VMessForm.resx
similarity index 100%
rename from Netch/Forms/Server/Vmess.resx
rename to Netch/ServerEx/VMess/Form/VMessForm.resx
diff --git a/Netch/Models/Information/VMess.cs b/Netch/ServerEx/VMess/Models/VMessConfig.cs
similarity index 98%
rename from Netch/Models/Information/VMess.cs
rename to Netch/ServerEx/VMess/Models/VMessConfig.cs
index 32133e67..c08b6a93 100644
--- a/Netch/Models/Information/VMess.cs
+++ b/Netch/ServerEx/VMess/Models/VMessConfig.cs
@@ -1,8 +1,8 @@
using System.Collections.Generic;
-namespace Netch.Models.Information
+namespace Netch.ServerEx.VMess.Models
{
- public class VMess
+ public class VMessConfig
{
public class InboundSettings
{
@@ -203,4 +203,4 @@ namespace Netch.Models.Information
public Routing routing;
}
}
-}
+}
\ No newline at end of file
diff --git a/Netch/Models/VMess.cs b/Netch/ServerEx/VMess/Models/VMessJObject.cs
similarity index 95%
rename from Netch/Models/VMess.cs
rename to Netch/ServerEx/VMess/Models/VMessJObject.cs
index 22c9550f..261a81a8 100644
--- a/Netch/Models/VMess.cs
+++ b/Netch/ServerEx/VMess/Models/VMessJObject.cs
@@ -1,9 +1,9 @@
-namespace Netch.Models
+namespace Netch.ServerEx.VMess.Models
{
///
/// 使用 v2rayN 定义的 VMess 链接格式
///
- public class VMess
+ public class VMessJObject
{
///
/// Mux Class
@@ -73,4 +73,4 @@
///
public Mux mux;
}
-}
+}
\ No newline at end of file
diff --git a/Netch/ServerEx/VMess/VMess.cs b/Netch/ServerEx/VMess/VMess.cs
new file mode 100644
index 00000000..a1bdf7f0
--- /dev/null
+++ b/Netch/ServerEx/VMess/VMess.cs
@@ -0,0 +1,118 @@
+using System.Collections.Generic;
+using Netch.Models;
+
+namespace Netch.ServerEx.VMess
+{
+ public class VMess : Server
+ {
+ public VMess()
+ {
+ Type = "VMess";
+ }
+
+ ///
+ /// 用户 ID
+ ///
+ public string UserID { get; set; }
+
+ ///
+ /// 额外 ID
+ ///
+ public int AlterID { get; set; }
+
+ ///
+ /// 加密方式
+ ///
+ public string EncryptMethod { get; set; } = VMessGlobal.EncryptMethods[0];
+
+
+ ///
+ /// 传输协议
+ ///
+ public string TransferProtocol { get; set; } = VMessGlobal.TransferProtocols[0];
+
+ ///
+ /// 伪装类型
+ ///
+ public string FakeType { get; set; } = VMessGlobal.FakeTypes[0];
+
+ ///
+ /// QUIC
+ ///
+ public string QUIC { get; set; } = VMessGlobal.QUIC[0];
+
+ ///
+ /// 伪装域名
+ ///
+ public string Host { get; set; }
+
+ ///
+ /// 传输路径
+ ///
+ public string Path { get; set; }
+
+ ///
+ /// QUIC 加密方式
+ ///
+ public string QUICSecure { get; set; } = VMessGlobal.QUIC[0];
+
+ ///
+ /// QUIC 加密密钥
+ ///
+ public string QUICSecret { get; set; } = string.Empty;
+
+ ///
+ /// TLS 底层传输安全
+ ///
+ public bool TLSSecure { get; set; } = false;
+
+ ///
+ /// Mux 多路复用
+ ///
+ public bool UseMux { get; set; } = true;
+ }
+
+ public class VMessGlobal
+ {
+ public static readonly List EncryptMethods = new List
+ {
+ "auto",
+ "none",
+ "aes-128-gcm",
+ "chacha20-poly1305"
+ };
+
+ public static readonly List QUIC = new List
+ {
+ "none",
+ "aes-128-gcm",
+ "chacha20-poly1305"
+ };
+
+ ///
+ /// V2Ray 传输协议
+ ///
+ public static readonly List TransferProtocols = new List
+ {
+ "tcp",
+ "kcp",
+ "ws",
+ "h2",
+ "quic"
+ };
+
+ ///
+ /// V2Ray 伪装类型
+ ///
+ public static readonly List FakeTypes = new List
+ {
+ "none",
+ "http",
+ "srtp",
+ "utp",
+ "wechat-video",
+ "dtls",
+ "wireguard"
+ };
+ }
+}
\ No newline at end of file
diff --git a/Netch/Controllers/EncryptedProxy/VMessController.cs b/Netch/ServerEx/VMess/VMessController.cs
similarity index 73%
rename from Netch/Controllers/EncryptedProxy/VMessController.cs
rename to Netch/ServerEx/VMess/VMessController.cs
index 18f72f62..4eb5850e 100644
--- a/Netch/Controllers/EncryptedProxy/VMessController.cs
+++ b/Netch/ServerEx/VMess/VMessController.cs
@@ -1,12 +1,12 @@
using System.Collections.Generic;
using System.IO;
using Netch.Models;
+using Netch.ServerEx.VMess.Models;
using Newtonsoft.Json;
-using VMess = Netch.Models.Information.VMess;
-namespace Netch.Controllers
+namespace Netch.ServerEx.VMess
{
- public class VMessController : EncryptedProxy
+ public class VMessController : ServerController
{
public VMessController()
{
@@ -16,34 +16,35 @@ namespace Netch.Controllers
StoppedKeywords.AddRange(new[] {"config file not readable", "failed to"});
}
- public override bool Start(Server server, Mode mode)
+ public override bool Start(Server s, Mode mode)
{
- File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new VMess.Config
+ var server = (VMess) s;
+ File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new VMessConfig.Config()
{
- inbounds = new List
+ inbounds = new List
{
- new VMess.Inbounds
+ new VMessConfig.Inbounds
{
- settings = new VMess.InboundSettings(),
+ settings = new VMessConfig.InboundSettings(),
port = Socks5LocalPort,
listen = LocalAddress
}
},
- outbounds = new List
+ outbounds = new List
{
- new VMess.Outbounds
+ new VMessConfig.Outbounds
{
- settings = new VMess.OutboundSettings
+ settings = new VMessConfig.OutboundSettings
{
- vnext = new List
+ vnext = new List
{
- new VMess.VNext
+ new VMessConfig.VNext
{
address = server.Hostname,
port = server.Port,
- users = new List
+ users = new List
{
- new VMess.User
+ new VMessConfig.User
{
id = server.UserID,
alterId = server.AlterID,
@@ -53,30 +54,30 @@ namespace Netch.Controllers
}
}
},
- streamSettings = new VMess.StreamSettings
+ streamSettings = new VMessConfig.StreamSettings
{
network = server.TransferProtocol,
security = server.TLSSecure ? "tls" : string.Empty,
wsSettings = server.TransferProtocol == "ws"
- ? new VMess.WebSocketSettings
+ ? new VMessConfig.WebSocketSettings
{
path = server.Path == string.Empty ? "/" : server.Path,
- headers = new VMess.WSHeaders
+ headers = new VMessConfig.WSHeaders
{
Host = server.Host == string.Empty ? server.Hostname : server.Host
}
}
: null,
tcpSettings = server.FakeType == "http"
- ? new VMess.TCPSettings
+ ? new VMessConfig.TCPSettings
{
- header = new VMess.TCPHeaders
+ header = new VMessConfig.TCPHeaders
{
type = server.FakeType,
- request = new VMess.TCPRequest
+ request = new VMessConfig.TCPRequest
{
path = server.Path == string.Empty ? "/" : server.Path,
- headers = new VMess.TCPRequestHeaders
+ headers = new VMessConfig.TCPRequestHeaders
{
Host = server.Host == string.Empty ? server.Hostname : server.Host
}
@@ -85,61 +86,61 @@ namespace Netch.Controllers
}
: null,
kcpSettings = server.TransferProtocol == "kcp"
- ? new VMess.KCPSettings
+ ? new VMessConfig.KCPSettings
{
- header = new VMess.TCPHeaders
+ header = new VMessConfig.TCPHeaders
{
type = server.FakeType
}
}
: null,
quicSettings = server.TransferProtocol == "quic"
- ? new VMess.QUICSettings
+ ? new VMessConfig.QUICSettings
{
security = server.QUICSecure,
key = server.QUICSecret,
- header = new VMess.TCPHeaders
+ header = new VMessConfig.TCPHeaders
{
type = server.FakeType
}
}
: null,
httpSettings = server.TransferProtocol == "h2"
- ? new VMess.HTTPSettings
+ ? new VMessConfig.HTTPSettings
{
host = server.Host == "" ? server.Hostname : server.Host,
path = server.Path == "" ? "/" : server.Path
}
: null,
- tlsSettings = new VMess.TLSSettings
+ tlsSettings = new VMessConfig.TLSSettings
{
allowInsecure = true,
serverName = server.Host == "" ? server.Hostname : server.Host
}
},
- mux = new VMess.OutboundMux
+ mux = new VMessConfig.OutboundMux
{
enabled = server.UseMux
}
},
mode.Type == 0 || mode.Type == 1 || mode.Type == 2
- ? new VMess.Outbounds
+ ? new VMessConfig.Outbounds
{
tag = "TUNTAP",
protocol = "freedom"
}
- : new VMess.Outbounds
+ : new VMessConfig.Outbounds
{
tag = "direct",
protocol = "freedom"
}
},
- routing = new VMess.Routing
+ routing = new VMessConfig.Routing
{
- rules = new List
+ rules = new List
{
mode.BypassChina
- ? new VMess.RoutingRules
+ ? new VMessConfig.RoutingRules
{
type = "field",
ip = new List
@@ -153,7 +154,7 @@ namespace Netch.Controllers
},
outboundTag = "direct"
}
- : new VMess.RoutingRules
+ : new VMessConfig.RoutingRules
{
type = "field",
ip = new List
diff --git a/Netch/ServerEx/VMess/VMessUtil.cs b/Netch/ServerEx/VMess/VMessUtil.cs
new file mode 100644
index 00000000..05d33f97
--- /dev/null
+++ b/Netch/ServerEx/VMess/VMessUtil.cs
@@ -0,0 +1,151 @@
+using System.Collections.Generic;
+using Netch.Models;
+using Netch.ServerEx.VMess.Form;
+using Netch.ServerEx.VMess.Models;
+using Netch.Utils;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Netch.ServerEx.VMess
+{
+ public class VMessUtil : IServerUtil
+ {
+ public ushort Priority { get; } = 2;
+ public string TypeName { get; } = "VMess";
+ public string FullName { get; } = "VMess";
+ public string[] UriScheme { get; } = {"vmess"};
+
+ public Server ParseJObject(JObject j)
+ {
+ return j.ToObject();
+ }
+
+ public void Edit(Server s)
+ {
+ new VMessForm(s).ShowDialog();
+ }
+
+ public void Create()
+ {
+ new VMessForm().ShowDialog();
+ }
+
+ public string GetShareLink(Server s)
+ {
+ var server = (VMess) s;
+
+ var vmessJson = JsonConvert.SerializeObject(new
+ {
+ 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.TLSSecure ? "tls" : ""
+ });
+ return "vmess://" + ShareLink.URLSafeBase64Encode(vmessJson);
+ }
+
+ public ServerController GetController()
+ {
+ return new VMessController();
+ }
+
+ public IEnumerable ParseUri(string text)
+ {
+ var data = new VMess();
+
+ text = text.Substring(8);
+ var vmess = JsonConvert.DeserializeObject(ShareLink.URLSafeBase64Decode(text));
+
+ data.Remark = vmess.ps;
+ data.Hostname = vmess.add;
+ data.Port = vmess.port;
+ data.UserID = vmess.id;
+ data.AlterID = vmess.aid;
+ data.TransferProtocol = vmess.net;
+ data.FakeType = vmess.type;
+
+ if (vmess.v == null || vmess.v == "1")
+ {
+ var info = vmess.host.Split(';');
+ if (info.Length == 2)
+ {
+ vmess.host = info[0];
+ vmess.path = info[1];
+ }
+ }
+
+ if (data.TransferProtocol == "quic")
+ {
+ if (VMessGlobal.QUIC.Contains(vmess.host))
+ {
+ data.QUICSecure = vmess.host;
+ data.QUICSecret = vmess.path;
+ }
+ }
+ else
+ {
+ data.Host = vmess.host;
+ data.Path = vmess.path;
+ }
+
+ data.TLSSecure = vmess.tls == "tls";
+
+ if (vmess.mux == null)
+ {
+ data.UseMux = false;
+ }
+ else
+ {
+ if (vmess.mux.enabled is bool enabled)
+ {
+ data.UseMux = enabled;
+ }
+ else if (vmess.mux.enabled is string muxEnabled)
+ {
+ data.UseMux = muxEnabled == "true"; // 针对使用字符串当作布尔值的情况
+ }
+ else
+ {
+ data.UseMux = false;
+ }
+ }
+
+ data.EncryptMethod = "auto"; // V2Ray 加密方式不包括在链接中,主动添加一个
+ return CheckServer(data) ? new[] {data} : null;
+ }
+
+ public bool CheckServer(Server s)
+ {
+ var server = (VMess) s;
+ if (!VMessGlobal.TransferProtocols.Contains(server.TransferProtocol))
+ {
+ Logging.Error($"不支持的 VMess 传输协议:{server.TransferProtocol}");
+ return false;
+ }
+
+ if (server.FakeType.Length != 0 && !VMessGlobal.FakeTypes.Contains(server.FakeType))
+ {
+ Logging.Error($"不支持的 VMess 伪装类型:{server.FakeType}");
+ return false;
+ }
+
+ if (server.TransferProtocol == "quic")
+ {
+ if (!VMessGlobal.QUIC.Contains(server.QUICSecure))
+ {
+ Logging.Error($"不支持的 VMess QUIC 加密方式:{server.QUICSecure}");
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Netch/Utils/Bandwidth.cs b/Netch/Utils/Bandwidth.cs
index e1fc1530..50c99eb2 100644
--- a/Netch/Utils/Bandwidth.cs
+++ b/Netch/Utils/Bandwidth.cs
@@ -73,9 +73,9 @@ namespace Netch.Utils
{
instances.Add(Process.GetCurrentProcess());
}
- else if (MainController.EncryptedProxyController != null)
+ else if (MainController.ServerController != null)
{
- instances.Add(MainController.EncryptedProxyController.Instance);
+ instances.Add(MainController.ServerController.Instance);
}
else if (MainController.ModeController != null)
{
diff --git a/Netch/Utils/Configuration.cs b/Netch/Utils/Configuration.cs
index cdb1950a..b6a0522a 100644
--- a/Netch/Utils/Configuration.cs
+++ b/Netch/Utils/Configuration.cs
@@ -1,7 +1,7 @@
-using System.Diagnostics;
-using System.IO;
+using System.IO;
using Netch.Models;
using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
namespace Netch.Utils
{
@@ -26,45 +26,26 @@ namespace Netch.Utils
{
try
{
- Global.Settings = JsonConvert.DeserializeObject(File.ReadAllText(SETTINGS_JSON));
- if (Global.Settings.Server != null && Global.Settings.Server.Count > 0)
+ var settingJObject = (JObject) JsonConvert.DeserializeObject(File.ReadAllText(SETTINGS_JSON));
+ Global.Settings = settingJObject?.ToObject() ?? new Setting();
+ Global.Settings.Server.Clear();
+
+ foreach (JObject server in settingJObject["Server"])
{
- // 如果是旧版 Server 类,使用旧版 Server 类进行读取
- if (Global.Settings.Server[0].Hostname == null)
- {
- var LegacySettingConfig = JsonConvert.DeserializeObject(File.ReadAllText(SETTINGS_JSON));
- for (var i = 0; i < LegacySettingConfig.Server.Count; i++)
- {
- Global.Settings.Server[i].Hostname = LegacySettingConfig.Server[i].Address;
- if (Global.Settings.Server[i].Type == "Shadowsocks")
- {
- Global.Settings.Server[i].Type = "SS";
- Global.Settings.Server[i].Plugin = LegacySettingConfig.Server[i].OBFS;
- Global.Settings.Server[i].PluginOption = LegacySettingConfig.Server[i].OBFSParam;
- }
- else if (Global.Settings.Server[i].Type == "ShadowsocksR")
- {
- Global.Settings.Server[i].Type = "SSR";
- }
- else if (Global.Settings.Server[i].Type == "VMess")
- {
- Global.Settings.Server[i].QUICSecure = LegacySettingConfig.Server[i].QUICSecurity;
- }
- }
- }
+ var serverResult = Servers.ParseJObject(server);
+ if (serverResult != null)
+ Global.Settings.Server.Add(serverResult);
}
}
catch (JsonException)
{
-
}
}
else
{
// 弹出提示
i18N.Load("System");
- // MessageBoxX.Show(i18N.Translate("If this is your first time using this software,\n please check https://netch.org to install supports first,\n or the program may report errors."));
// 创建 data 文件夹并保存默认设置
Save();
@@ -80,35 +61,8 @@ namespace Netch.Utils
{
Directory.CreateDirectory(DATA_DIR);
}
+
File.WriteAllText(SETTINGS_JSON, JsonConvert.SerializeObject(Global.Settings, Formatting.Indented));
}
-
- ///
- /// 安装tap网卡
- ///
- public static void addtap()
- {
- Logging.Info("正在安装 TUN/TAP 适配器");
- //安装Tap Driver
- var installProcess = new Process();
- installProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
- installProcess.StartInfo.FileName = Path.Combine("bin/tap-driver", "addtap.bat");
- installProcess.Start();
- installProcess.WaitForExit();
- installProcess.Close();
- }
- ///
- /// 卸载tap网卡
- ///
- public static void deltapall()
- {
- Logging.Info("正在卸载 TUN/TAP 适配器");
- var installProcess = new Process();
- installProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
- installProcess.StartInfo.FileName = Path.Combine("bin/tap-driver", "deltapall.bat");
- installProcess.Start();
- installProcess.WaitForExit();
- installProcess.Close();
- }
}
-}
+}
\ No newline at end of file
diff --git a/Netch/Utils/Extensions.cs b/Netch/Utils/Extensions.cs
deleted file mode 100644
index 933b9021..00000000
--- a/Netch/Utils/Extensions.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System.Collections.Generic;
-using System.IO;
-
-namespace Netch.Utils
-{
- public static class Extensions
- {
- public static IEnumerable GetLines(this string str, bool removeEmptyLines = true)
- {
- using var sr = new StringReader(str);
- string line;
- while ((line = sr.ReadLine()) != null)
- {
- if (removeEmptyLines && string.IsNullOrWhiteSpace(line))
- {
- continue;
- }
-
- yield return line;
- }
- }
- }
-}
diff --git a/Netch/Utils/Firewall.cs b/Netch/Utils/Firewall.cs
index 5042e629..9a0233ff 100644
--- a/Netch/Utils/Firewall.cs
+++ b/Netch/Utils/Firewall.cs
@@ -11,12 +11,10 @@ namespace Netch.Utils
{
"bin/NTT.exe",
"bin/Privoxy.exe",
- "bin/Redirector.exe",
"bin/Shadowsocks.exe",
"bin/ShadowsocksR.exe",
"bin/Trojan.exe",
"bin/tun2socks.exe",
- "bin/unbound.exe",
"bin/v2ray.exe",
"Netch.exe"
};
diff --git a/Netch/Utils/OnlyInstance.cs b/Netch/Utils/OnlyInstance.cs
index bbaf4843..ce5dd142 100644
--- a/Netch/Utils/OnlyInstance.cs
+++ b/Netch/Utils/OnlyInstance.cs
@@ -12,7 +12,7 @@ namespace Netch.Utils
Show,
Exit
}
-
+
public static event EventHandler Called;
private static void OnCalled(Commands e)
diff --git a/Netch/Utils/Servers.cs b/Netch/Utils/Servers.cs
new file mode 100644
index 00000000..de0d4140
--- /dev/null
+++ b/Netch/Utils/Servers.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Netch.Models;
+using Newtonsoft.Json.Linq;
+
+namespace Netch.Utils
+{
+ public static class Servers
+ {
+ public static readonly IEnumerable ServerUtils;
+
+ static Servers()
+ {
+ var serversUtilsTypes = Assembly.GetExecutingAssembly().GetExportedTypes().Where(type => type.GetInterfaces().Any(t => t == typeof(IServerUtil)));
+ ServerUtils = serversUtilsTypes.Select(t => (IServerUtil) Activator.CreateInstance(t)).OrderBy(util => util.Priority);
+ }
+
+ public static Server ParseJObject(JObject o)
+ {
+ var handle = GetUtilByTypeName((string) o["Type"]);
+ if (handle == null)
+ {
+ Logging.Warning($"不支持的服务器类型: {o["Type"]}");
+ return null;
+ }
+
+ return handle.ParseJObject(o);
+ }
+
+ public static IServerUtil GetUtilByTypeName(string typeName)
+ {
+ if (string.IsNullOrEmpty(typeName))
+ return null;
+ return ServerUtils.FirstOrDefault(i => (i.TypeName ?? "").Equals(typeName));
+ }
+
+ public static IServerUtil GetUtilByFullName(string fullName)
+ {
+ if (string.IsNullOrEmpty(fullName))
+ return null;
+ return ServerUtils.FirstOrDefault(i => (i.FullName ?? "").Equals(fullName));
+ }
+
+ public static IServerUtil GetUtilByUriScheme(string typeName)
+ {
+ return ServerUtils.FirstOrDefault(i => i.UriScheme.Any(s => s.Equals(typeName)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Netch/Utils/ShareLink.cs b/Netch/Utils/ShareLink.cs
index 8db51bfb..f4a73737 100644
--- a/Netch/Utils/ShareLink.cs
+++ b/Netch/Utils/ShareLink.cs
@@ -1,28 +1,30 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Text;
-using System.Text.RegularExpressions;
-using System.Web;
-using Netch.Models;
-using Netch.Models.SS;
-using Netch.Models.SSD;
+using Netch.ServerEx.Shadowsocks;
+using Netch.ServerEx.Shadowsocks.Models;
using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
using Server = Netch.Models.Server;
namespace Netch.Utils
{
public static class ShareLink
{
+ #region Utils
+
///
- /// URL 传输安全的 Base64 解码
- ///
- /// 需要解码的字符串
- /// 解码后的字符串
+ /// URL 传输安全的 Base64 解码
+ ///
+ /// 需要解码的字符串
+ /// 解码后的字符串
public static string URLSafeBase64Decode(string text)
{
return Encoding.UTF8.GetString(Convert.FromBase64String(text.Replace("-", "+").Replace("_", "/").PadRight(text.Length + (4 - text.Length % 4) % 4, '=')));
}
+
///
/// URL 传输安全的 Base64 加密
///
@@ -33,82 +35,111 @@ namespace Netch.Utils
return Convert.ToBase64String(Encoding.UTF8.GetBytes(text)).Replace("+", "-").Replace("/", "_").Replace("=", "");
}
- ///
- /// 根据服务器生成分享链接
- ///
- /// 需要获取分享链接的服务器
- /// 解码后的字符串
- public static string GetShareLink(Server server)
+ private static string RemoveEmoji(string text)
{
- var retLinkStr = "";
- switch (server.Type)
- {
- case "Socks5":
- // https://t.me/socks?server=1.1.1.1&port=443
- retLinkStr = string.Format("https://t.me/socks?server={0}&port={1}", server.Hostname, server.Port);
-
- break;
- case "SS":
- // ss://method:password@server:port#Remark
- retLinkStr = "ss://" + URLSafeBase64Encode(string.Format("{0}:{1}@{2}:{3}", server.EncryptMethod, server.Password, server.Hostname, server.Port)) + "#" + HttpUtility.UrlEncode(server.Remark);
-
- break;
- case "SSR":
- // https://github.com/shadowsocksr-backup/shadowsocks-rss/wiki/SSR-QRcode-scheme
- // ssr://base64(host:port:protocol:method:obfs:base64pass/?obfsparam=base64param&protoparam=base64param&remarks=base64remarks&group=base64group&udpport=0&uot=0)
- var paraStr = string.Format("/?obfsparam={0}&protoparam={1}&remarks={2}", URLSafeBase64Encode(server.OBFSParam), URLSafeBase64Encode(server.ProtocolParam), URLSafeBase64Encode(server.Remark));
- retLinkStr = "ssr://" + URLSafeBase64Encode(string.Format("{0}:{1}:{2}:{3}:{4}:{5}{6}", server.Hostname, server.Port, server.Protocol, server.EncryptMethod, server.OBFS, URLSafeBase64Encode(server.Password), paraStr));
-
- break;
- case "VMess":
- var vmessJson = JsonConvert.SerializeObject(new
- {
- 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.TLSSecure ? "tls" : ""
- });
- retLinkStr = "vmess://" + URLSafeBase64Encode(vmessJson);
-
- break;
- default:
- return null;
- }
- return retLinkStr;
+ byte[] emojiBytes = {240, 159};
+ var remark = Encoding.UTF8.GetBytes(text);
+ var startIndex = 0;
+ while (remark.Length > startIndex + 1 && remark[startIndex] == emojiBytes[0] && remark[startIndex + 1] == emojiBytes[1])
+ startIndex += 4;
+ return Encoding.UTF8.GetString(remark.Skip(startIndex).ToArray()).Trim();
}
- public static List Parse(string text)
+
+ public static string UnBase64String(string value)
{
+ if (string.IsNullOrEmpty(value))
+ {
+ return "";
+ }
+
+ var bytes = Convert.FromBase64String(value);
+ return Encoding.UTF8.GetString(bytes);
+ }
+
+ public static string ToBase64String(string value)
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ return "";
+ }
+
+ var bytes = Encoding.UTF8.GetBytes(value);
+ return Convert.ToBase64String(bytes);
+ }
+
+ public static Dictionary ParseParam(string paramStr)
+ {
+ var paramsDict = new Dictionary();
+ var obfsParams = paramStr.Split('&');
+ foreach (var p in obfsParams)
+ {
+ if (p.IndexOf('=') > 0)
+ {
+ var index = p.IndexOf('=');
+ var key = p.Substring(0, index);
+ var val = p.Substring(index + 1);
+ paramsDict[key] = val;
+ }
+ }
+
+ return paramsDict;
+ }
+
+ public static IEnumerable GetLines(this string str, bool removeEmptyLines = true)
+ {
+ using var sr = new StringReader(str);
+ string line;
+ while ((line = sr.ReadLine()) != null)
+ {
+ if (removeEmptyLines && string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
+
+ yield return line;
+ }
+ }
+
+ #endregion
+
+ public static string GetShareLink(Server server)
+ {
+ return Servers.GetUtilByTypeName(server.Type).GetShareLink(server);
+ }
+
+ public static List ParseText(string text)
+ {
+ try
+ {
+ text = URLSafeBase64Decode(text);
+ }
+ catch
+ {
+ // ignored
+ }
+
var list = new List();
try
{
try
{
- var ssServers = JsonConvert.DeserializeObject>(text);
- list.AddRange(ssServers.Select(shadowsocksServer => new Server
+ list.AddRange(JsonConvert.DeserializeObject>(text).Select(server => new Shadowsocks
{
- Type = "SS",
- Hostname = shadowsocksServer.server,
- Port = shadowsocksServer.server_port,
- EncryptMethod = shadowsocksServer.method,
- Password = shadowsocksServer.password,
- Remark = shadowsocksServer.remarks,
- Plugin = shadowsocksServer.plugin,
- PluginOption = shadowsocksServer.plugin_opts
+ Hostname = server.server,
+ Port = server.server_port,
+ EncryptMethod = server.method,
+ Password = server.password,
+ Remark = server.remarks,
+ Plugin = server.plugin,
+ PluginOption = server.plugin_opts
}));
}
catch (JsonReaderException)
{
foreach (var line in text.GetLines())
{
- var servers = ParseLine(line);
+ var servers = ParseUri(line);
if (servers != null)
{
list.AddRange(servers);
@@ -130,7 +161,7 @@ namespace Netch.Utils
return list;
}
- private static IEnumerable ParseLine(string text)
+ private static IEnumerable ParseUri(string text)
{
var list = new List();
@@ -138,356 +169,23 @@ namespace Netch.Utils
{
if (text.StartsWith("tg://socks?") || text.StartsWith("https://t.me/socks?"))
{
- var data = new Server();
- data.Type = "Socks5";
-
- var dict = new Dictionary();
- foreach (var str in text.Replace("tg://socks?", "").Replace("https://t.me/socks?", "").Split('&'))
- {
- var splited = str.Split('=');
-
- dict.Add(splited[0], splited[1]);
- }
-
- if (!dict.ContainsKey("server") || !dict.ContainsKey("port"))
- {
- return null;
- }
-
- data.Hostname = dict["server"];
- data.Port = int.Parse(dict["port"]);
- if (dict.ContainsKey("user") && !string.IsNullOrWhiteSpace(dict["user"]))
- {
- data.Username = dict["user"];
- }
- if (dict.ContainsKey("pass") && !string.IsNullOrWhiteSpace(dict["pass"]))
- {
- data.Password = dict["pass"];
- }
-
- list.Add(data);
- }
- else if (text.StartsWith("ss://"))
- {
- var data = new Server();
- data.Type = "SS";
-
- text = text.Replace("/?", "?");
- try
- {
- if (text.Contains("#"))
- {
- data.Remark = HttpUtility.UrlDecode(text.Split('#')[1]);
- text = text.Split('#')[0];
- }
- if (text.Contains("?"))
- {
- var finder = new Regex(@"^(?.+?)\?(.+)$");
- var match = finder.Match(text);
-
- if (match.Success)
- {
- var plugins = HttpUtility.UrlDecode(HttpUtility.ParseQueryString(new Uri(text).Query).Get("plugin"));
- if (plugins != null)
- {
- var plugin = plugins.Substring(0, plugins.IndexOf(";", StringComparison.Ordinal));
- var pluginopts = plugins.Substring(plugins.IndexOf(";", StringComparison.Ordinal) + 1);
- if (plugin == "obfs-local" || plugin == "simple-obfs")
- {
- plugin = "simple-obfs";
- if (!pluginopts.Contains("obfs="))
- pluginopts = "obfs=http;obfs-host=" + pluginopts;
- }
- else if (plugin == "simple-obfs-tls")
- {
- plugin = "simple-obfs";
- if (!pluginopts.Contains("obfs="))
- pluginopts = "obfs=tls;obfs-host=" + pluginopts;
- }
-
- data.Plugin = plugin;
- data.PluginOption = pluginopts;
- }
-
- text = match.Groups["data"].Value;
- }
- else
- {
- throw new FormatException();
- }
- }
- if (text.Contains("@"))
- {
- var finder = new Regex(@"^ss://(?.+?)@(?.+):(?\d+)");
- var parser = new Regex(@"^(?.+?):(?.+)$");
- var match = finder.Match(text);
- if (!match.Success)
- {
- throw new FormatException();
- }
-
- data.Hostname = match.Groups["server"].Value;
- data.Port = int.Parse(match.Groups["port"].Value);
-
- var base64 = URLSafeBase64Decode(match.Groups["base64"].Value);
- match = parser.Match(base64);
- if (!match.Success)
- {
- throw new FormatException();
- }
-
- data.EncryptMethod = match.Groups["method"].Value;
- data.Password = match.Groups["password"].Value;
- }
- else
- {
- var parser = new Regex(@"^((?.+?):(?.+)@(?.+):(?\d+))");
- var match = parser.Match(URLSafeBase64Decode(text.Replace("ss://", "")));
- if (!match.Success)
- {
- throw new FormatException();
- }
-
- data.Hostname = match.Groups["server"].Value;
- data.Port = int.Parse(match.Groups["port"].Value);
- data.EncryptMethod = match.Groups["method"].Value;
- data.Password = match.Groups["password"].Value;
- }
-
- if (!Global.EncryptMethods.SS.Contains(data.EncryptMethod))
- {
- Logging.Error($"不支持的 SS 加密方式:{data.EncryptMethod}");
- return null;
- }
-
- list.Add(data);
- }
- catch (FormatException)
- {
- return null;
- }
- }
- else if (text.StartsWith("ssd://"))
- {
- var json = JsonConvert.DeserializeObject(URLSafeBase64Decode(text.Substring(6)));
-
- foreach (var server in json.servers)
- {
- var data = new Server();
- data.Type = "SS";
-
- data.Remark = server.remarks;
- data.Hostname = server.server;
- data.Port = server.port != 0 ? server.port : json.port;
- data.Password = server.password != null ? server.password : json.password;
- data.EncryptMethod = server.encryption != null ? server.encryption : json.encryption;
- data.Plugin = string.IsNullOrEmpty(json.plugin) ? string.IsNullOrEmpty(server.plugin) ? null : server.plugin : json.plugin;
- data.PluginOption = string.IsNullOrEmpty(json.plugin_options) ? string.IsNullOrEmpty(server.plugin_options) ? null : server.plugin_options : json.plugin_options;
-
- if (Global.EncryptMethods.SS.Contains(data.EncryptMethod))
- {
- list.Add(data);
- }
- }
- }
- else if (text.StartsWith("ssr://"))
- {
-
- list.Add(SsrServerFromLink(text));
-
- }
- else if (text.StartsWith("vmess://"))
- {
- var data = new Server();
- data.Type = "VMess";
-
- text = text.Substring(8);
- var vmess = JsonConvert.DeserializeObject(URLSafeBase64Decode(text));
-
- data.Remark = vmess.ps;
- data.Hostname = vmess.add;
- data.Port = vmess.port;
- data.UserID = vmess.id;
- data.AlterID = vmess.aid;
-
- data.TransferProtocol = vmess.net;
- if (!Global.TransferProtocols.Contains(data.TransferProtocol))
- {
- Logging.Error($"不支持的 VMess 传输协议:{data.TransferProtocol}");
- return null;
- }
-
- data.FakeType = vmess.type;
- if (data.FakeType.Length != 0 && !Global.FakeTypes.Contains(data.FakeType))
- {
- Logging.Error($"不支持的 VMess 伪装类型:{data.FakeType}");
- return null;
- }
-
- if (vmess.v == null || vmess.v == "1")
- {
- var info = vmess.host.Split(';');
- if (info.Length == 2)
- {
- vmess.host = info[0];
- vmess.path = info[1];
- }
- }
- if (data.TransferProtocol == "quic")
- {
- if (!Global.EncryptMethods.VMessQUIC.Contains(vmess.host))
- {
- Logging.Error($"不支持的 VMess QUIC 加密方式:{vmess.host}");
- return null;
- }
-
- data.QUICSecure = vmess.host;
- data.QUICSecret = vmess.path;
-
- }
- else
- {
- data.Host = vmess.host;
- data.Path = vmess.path;
- }
- data.TLSSecure = vmess.tls == "tls";
-
- if (vmess.mux == null)
- {
- data.UseMux = false;
- }
- else
- {
- if (vmess.mux.enabled is bool enabled)
- {
- data.UseMux = enabled;
- }
- else if (vmess.mux.enabled is string muxEnabled)
- {
- data.UseMux = muxEnabled == "true"; // 针对使用字符串当作布尔值的情况
- }
- else
- {
- data.UseMux = false;
- }
- }
-
- data.EncryptMethod = "auto"; // V2Ray 加密方式不包括在链接中,主动添加一个
-
- list.Add(data);
+ list.AddRange(Servers.GetUtilByTypeName("Socks5").ParseUri(text));
}
else if (text.StartsWith("Netch://"))
{
- text = text.Substring(8);
- var NetchLink = JsonConvert.DeserializeObject(URLSafeBase64Decode(text));
- if (!string.IsNullOrEmpty(NetchLink.Hostname) || NetchLink.Port > 65536 || NetchLink.Port > 0)
- {
- return null;
- }
-
- switch (NetchLink.Type)
- {
- case "Socks5":
- list.Add(NetchLink);
- break;
- case "SS":
- if (!Global.EncryptMethods.SS.Contains(NetchLink.EncryptMethod))
- {
- Logging.Error($"不支持的 SS 加密方式:{NetchLink.EncryptMethod}");
- return null;
- }
- break;
- case "SSR":
- if (!Global.EncryptMethods.SSR.Contains(NetchLink.EncryptMethod))
- {
- Logging.Error($"不支持的 SSR 加密方式:{NetchLink.EncryptMethod}");
- return null;
- }
- if (!Global.Protocols.Contains(NetchLink.Protocol))
- {
- Logging.Error($"不支持的 SSR 协议:{NetchLink.Protocol}");
- return null;
- }
- if (!Global.OBFSs.Contains(NetchLink.OBFS))
- {
- Logging.Error($"不支持的 SSR 混淆:{NetchLink.OBFS}");
- return null;
- }
- break;
- case "VMess":
- if (!Global.TransferProtocols.Contains(NetchLink.TransferProtocol))
- {
- Logging.Error($"不支持的 VMess 传输协议:{NetchLink.TransferProtocol}");
- return null;
- }
- if (!Global.FakeTypes.Contains(NetchLink.FakeType))
- {
- Logging.Error($"不支持的 VMess 伪装类型:{NetchLink.FakeType}");
- return null;
- }
- if (NetchLink.TransferProtocol == "quic")
- {
- if (!Global.EncryptMethods.VMessQUIC.Contains(NetchLink.QUICSecure))
- {
- Logging.Error($"不支持的 VMess QUIC 加密方式:{NetchLink.QUICSecure}");
- return null;
- }
- }
- break;
- default:
- return null;
- }
- list.Add(NetchLink);
+ list.Add(ParseNetchUri(text));
}
- else if (text.StartsWith("trojan://"))
+ else
{
- var data = new Server();
- data.Type = "Trojan";
-
- text = text.Replace("/?", "?");
- try
- {
- if (text.Contains("#"))
- {
- data.Remark = HttpUtility.UrlDecode(text.Split('#')[1]);
- text = text.Split('#')[0];
- }
- if (text.Contains("?"))
- {
- var reg = new Regex(@"^(?.+?)\?(.+)$");
- var regmatch = reg.Match(text);
-
- if (regmatch.Success)
- {
- var peer = HttpUtility.UrlDecode(HttpUtility.ParseQueryString(new Uri(text).Query).Get("peer"));
-
- if (peer != null)
- data.Host = peer;
-
- text = regmatch.Groups["data"].Value;
- }
- else
- {
- throw new FormatException();
- }
- }
- var finder = new Regex(@"^trojan://(?.+?)@(?.+):(?\d+)");
- var match = finder.Match(text);
- if (!match.Success)
- {
- throw new FormatException();
- }
-
- data.Password = match.Groups["psk"].Value;
- data.Hostname = match.Groups["server"].Value;
- data.Port = int.Parse(match.Groups["port"].Value);
-
- list.Add(data);
- }
- catch (FormatException)
+ var scheme = GetUriScheme(text);
+ var util = Servers.GetUtilByUriScheme(scheme);
+ if (util == null)
{
+ Logging.Warning($"无法处理 {scheme} 协议订阅链接");
return null;
}
+
+ list.AddRange(util.ParseUri(text));
}
}
catch (Exception e)
@@ -496,135 +194,49 @@ namespace Netch.Utils
return null;
}
- byte[] emoji_bytes = { 240, 159 };
foreach (var node in list)
{
- var remark = Encoding.UTF8.GetBytes(node.Remark);
- var start_index = 0;
- while (remark.Length > start_index + 1 && remark[start_index] == emoji_bytes[0] && remark[start_index + 1] == emoji_bytes[1])
- start_index += 4;
- node.Remark = Encoding.UTF8.GetString(remark.Skip(start_index).ToArray()).Trim();
+ node.Remark = RemoveEmoji(node.Remark);
}
- return list;
- }
- public static string UnBase64String(string value)
- {
- if (value == null || value == "")
- {
- return "";
- }
- var bytes = Convert.FromBase64String(value);
- return Encoding.UTF8.GetString(bytes);
- }
- public static string ToBase64String(string value)
- {
- if (value == null || value == "")
- {
- return "";
- }
- var bytes = Encoding.UTF8.GetBytes(value);
- return Convert.ToBase64String(bytes);
+ return list.Where(s => s != null);
}
- ///
- /// SSR链接解析器
- /// Copy From https://github.com/HMBSbige/ShadowsocksR-Windows/blob/d9dc8d032a6e04c14b9dc6c8f673c9cc5aa9f607/shadowsocks-csharp/Model/Server.cs#L428
- /// Thx :D
- ///
- ///
- ///
- public static Server SsrServerFromLink(string ssrUrl)
+ private static string GetUriScheme(string text)
{
- // ssr://host:port:protocol:method:obfs:base64pass/?obfsparam=base64&remarks=base64&group=base64&udpport=0&uot=1
- var ssr = Regex.Match(ssrUrl, "ssr://([A-Za-z0-9_-]+)", RegexOptions.IgnoreCase);
- if (!ssr.Success)
- throw new FormatException();
-
- var data = URLSafeBase64Decode(ssr.Groups[1].Value);
- var params_dict = new Dictionary();
-
- var param_start_pos = data.IndexOf("?", StringComparison.Ordinal);
- if (param_start_pos > 0)
- {
- params_dict = ParseParam(data.Substring(param_start_pos + 1));
- data = data.Substring(0, param_start_pos);
- }
- if (data.IndexOf("/", StringComparison.Ordinal) >= 0)
- {
- data = data.Substring(0, data.LastIndexOf("/", StringComparison.Ordinal));
- }
-
- var UrlFinder = new Regex("^(.+):([^:]+):([^:]*):([^:]+):([^:]*):([^:]+)");
- var match = UrlFinder.Match(data);
-
- if (match == null || !match.Success)
- throw new FormatException();
-
- var serverAddr = match.Groups[1].Value;
- var Server_Port = ushort.Parse(match.Groups[2].Value);
- var Protocol = match.Groups[3].Value.Length == 0 ? "origin" : match.Groups[3].Value;
- Protocol = Protocol.Replace("_compatible", "");
- var Method = match.Groups[4].Value;
- var obfs = match.Groups[5].Value.Length == 0 ? "plain" : match.Groups[5].Value;
- obfs = obfs.Replace("_compatible", "");
- var Password = URLSafeBase64Decode(match.Groups[6].Value);
- var ProtocolParam = "";
- var ObfsParam = "";
- var Remarks = "";
- var Group = "";
-
- if (params_dict.ContainsKey("protoparam"))
- {
- ProtocolParam = URLSafeBase64Decode(params_dict["protoparam"]);
- }
- if (params_dict.ContainsKey("obfsparam"))
- {
- ObfsParam = URLSafeBase64Decode(params_dict["obfsparam"]);
- }
- if (params_dict.ContainsKey("remarks"))
- {
- Remarks = URLSafeBase64Decode(params_dict["remarks"]);
- }
- Group = params_dict.ContainsKey("group") ? URLSafeBase64Decode(params_dict["group"]) : string.Empty;
-
- /*if (params_dict.ContainsKey("uot"))
- {
- UdpOverTcp = int.Parse(params_dict["uot"]) != 0;
- }
- if (params_dict.ContainsKey("udpport"))
- {
- Server_Udp_Port = ushort.Parse(params_dict["udpport"]);
- }*/
- var server = new Server();
- server.Type = "SSR";
- server.Hostname = serverAddr;
- server.Port = Server_Port;
- server.Protocol = Protocol;
- server.EncryptMethod = Method;
- server.OBFS = obfs;
- server.Password = Password;
- server.ProtocolParam = ProtocolParam;
- server.OBFSParam = ObfsParam;
- server.Remark = Remarks;
- server.Group = Group;
- return server;
+ var endIndex = text.IndexOf("://", StringComparison.Ordinal);
+ if (endIndex == -1)
+ throw new UriFormatException("Text is not a URI");
+ return text.Substring(0, endIndex);
}
- private static Dictionary ParseParam(string paramStr)
+
+ private static Server ParseNetchUri(string text)
{
- var paramsDict = new Dictionary();
- var obfsParams = paramStr.Split('&');
- foreach (var p in obfsParams)
+ text = text.Substring(8);
+ var NetchLink = (JObject) JsonConvert.DeserializeObject(URLSafeBase64Decode(text));
+ if (NetchLink == null)
{
- if (p.IndexOf('=') > 0)
- {
- var index = p.IndexOf('=');
- var key = p.Substring(0, index);
- var val = p.Substring(index + 1);
- paramsDict[key] = val;
- }
+ return null;
}
- return paramsDict;
+
+ if (string.IsNullOrEmpty((string) NetchLink["Hostname"]))
+ {
+ return null;
+ }
+
+ if (!ushort.TryParse((string) NetchLink["Port"], out _))
+ {
+ return null;
+ }
+
+ var type = (string) NetchLink["Type"];
+ var s = Servers.GetUtilByTypeName(type).ParseJObject(NetchLink);
+ return Servers.GetUtilByTypeName(s.Type).CheckServer(s) ? s : null;
+ }
+
+ public static string GetNetchLink(Server s)
+ {
+ return "Netch://" + URLSafeBase64Encode(JsonConvert.SerializeObject(s));
}
}
-}
+}
\ No newline at end of file
diff --git a/Netch/Utils/TUNTAP.cs b/Netch/Utils/TUNTAP.cs
index ca795950..49587b59 100644
--- a/Netch/Utils/TUNTAP.cs
+++ b/Netch/Utils/TUNTAP.cs
@@ -1,4 +1,6 @@
using System;
+using System.Diagnostics;
+using System.IO;
using Microsoft.Win32;
namespace Netch.Utils
@@ -62,5 +64,30 @@ namespace Netch.Utils
{
return false;
}
+
+ ///
+ /// 卸载tap网卡
+ ///
+ public static void deltapall()
+ {
+ Logging.Info("正在卸载 TUN/TAP 适配器");
+ var installProcess = new Process {StartInfo = {WindowStyle = ProcessWindowStyle.Hidden, FileName = Path.Combine("bin/tap-driver", "deltapall.bat")}};
+ installProcess.Start();
+ installProcess.WaitForExit();
+ installProcess.Close();
+ }
+
+ ///
+ /// 安装tap网卡
+ ///
+ public static void addtap()
+ {
+ Logging.Info("正在安装 TUN/TAP 适配器");
+ //安装Tap Driver
+ var installProcess = new Process {StartInfo = {WindowStyle = ProcessWindowStyle.Hidden, FileName = Path.Combine("bin/tap-driver", "addtap.bat")}};
+ installProcess.Start();
+ installProcess.WaitForExit();
+ installProcess.Close();
+ }
}
}
\ No newline at end of file
diff --git a/Netch/Utils/Utils.cs b/Netch/Utils/Utils.cs
index a44a88ad..38e678e8 100644
--- a/Netch/Utils/Utils.cs
+++ b/Netch/Utils/Utils.cs
@@ -1,7 +1,7 @@
using System;
-using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
+using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
@@ -9,6 +9,7 @@ using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
+using System.Windows.Forms;
using MaxMind.GeoIP2;
namespace Netch.Utils
@@ -172,7 +173,7 @@ namespace Netch.Utils
}
catch (Exception e)
{
- Logging.Error($"找不到出口IP所在网卡{e}");
+ Logging.Error($"找不到出口IP所在网卡: {e}");
return false;
}
}
@@ -182,5 +183,27 @@ namespace Netch.Utils
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(adapter => adapter.Id == id);
Logging.Warning($"检索此网卡信息出错: {adapter.Name} {adapter.Id} {adapter.Description}");
}
+
+ public static void DrawCenterComboBox(object sender, DrawItemEventArgs e)
+ {
+ if (sender is ComboBox cbx)
+ {
+ e.DrawBackground();
+
+ if (e.Index >= 0)
+ {
+ var brush = new SolidBrush(cbx.ForeColor);
+
+ if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
+ brush = SystemBrushes.HighlightText as SolidBrush;
+
+ e.Graphics.DrawString(cbx.Items[e.Index].ToString(), cbx.Font, brush, e.Bounds, new StringFormat
+ {
+ LineAlignment = StringAlignment.Center,
+ Alignment = StringAlignment.Center
+ });
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Netch/Utils/WMI.cs b/Netch/Utils/WMI.cs
deleted file mode 100644
index 37def91a..00000000
--- a/Netch/Utils/WMI.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.Management;
-
-namespace Netch.Utils
-{
- static internal class WMI
- {
- public static ManagementObject GetManagementObjectByDeviceNameOrDefault(string deviceName)
- {
- foreach (ManagementObject mo in new ManagementClass("Win32_NetworkAdapterConfiguration").GetInstances())
- {
- if (((string) mo["Caption"]).EndsWith(deviceName))
- {
- return mo;
- }
- }
-
- return null;
- }
- }
-}
\ No newline at end of file