diff --git a/Netch/Controllers/Guard.cs b/Netch/Controllers/Guard.cs index 5bd1e96d..d73bcb93 100644 --- a/Netch/Controllers/Guard.cs +++ b/Netch/Controllers/Guard.cs @@ -67,7 +67,9 @@ namespace Netch.Controllers { try { - if (Instance == null || Instance.HasExited) return; + if (Instance == null || Instance.HasExited) + return; + Instance.Kill(); Instance.WaitForExit(); } @@ -134,7 +136,10 @@ namespace Netch.Controllers Instance.Start(); if (priority != ProcessPriorityClass.Normal) Instance.PriorityClass = priority; - if (!RedirectStd) return; + + if (!RedirectStd) + return; + // 启动日志重定向 Instance.BeginOutputReadLine(); Instance.BeginErrorReadLine(); diff --git a/Netch/Controllers/HTTPController.cs b/Netch/Controllers/HTTPController.cs index 36a9283e..890cd1c1 100644 --- a/Netch/Controllers/HTTPController.cs +++ b/Netch/Controllers/HTTPController.cs @@ -45,6 +45,7 @@ namespace Netch.Controllers Server = $"127.0.0.1:{Global.Settings.HTTPLocalPort}", Bypass = string.Join(";", ProxyService.LanIp) }; + service.Global(); } } @@ -72,6 +73,7 @@ namespace Netch.Controllers service.Bypass = prevBypass; service.Global(); } + if (prevPAC != "") { service.AutoConfigUrl = prevPAC; @@ -89,6 +91,7 @@ namespace Netch.Controllers } }) }; + Task.WaitAll(tasks); } diff --git a/Netch/Controllers/MainController.cs b/Netch/Controllers/MainController.cs index 0b1c1458..ec21ae45 100644 --- a/Netch/Controllers/MainController.cs +++ b/Netch/Controllers/MainController.cs @@ -175,6 +175,7 @@ namespace Netch.Controllers Task.Run(() => ServerController?.Stop()), Task.Run(() => ModeController?.Stop()) }; + await Task.WhenAll(tasks); ModeController = null; ServerController = null; diff --git a/Netch/Controllers/NFController.cs b/Netch/Controllers/NFController.cs index 0ea38af8..4c89dde8 100644 --- a/Netch/Controllers/NFController.cs +++ b/Netch/Controllers/NFController.cs @@ -57,7 +57,8 @@ namespace Netch.Controllers aio_dial((int) NameList.TYPE_FILTERLOOPBACK, "false"); aio_dial((int) NameList.TYPE_TCPLISN, Global.Settings.RedirectorTCPPort.ToString()); - if (Global.Settings.ProcessNoProxyForUdp && Global.Settings.ProcessNoProxyForTcp) MessageBoxX.Show("?"); + if (Global.Settings.ProcessNoProxyForUdp && Global.Settings.ProcessNoProxyForTcp) + MessageBoxX.Show("?"); //UDP if (Global.Settings.ProcessNoProxyForUdp) @@ -96,6 +97,7 @@ namespace Netch.Controllers _sysDns = DNS.OutboundDNS; if (string.IsNullOrWhiteSpace(Global.Settings.ModifiedDNS)) Global.Settings.ModifiedDNS = "1.1.1.1,8.8.8.8"; + DNS.OutboundDNS = Global.Settings.ModifiedDNS; } @@ -138,6 +140,7 @@ namespace Netch.Controllers { if (r.StartsWith("!")) return aio_dial((int) NameList.TYPE_ADDNAME, r.Substring(1)); + return aio_dial((int) NameList.TYPE_ADDNAME, r); } finally @@ -177,7 +180,8 @@ namespace Netch.Controllers reinstallFlag = true; } - if (!reinstallFlag) return; + if (!reinstallFlag) + return; Logging.Info("更新驱动"); UninstallDriver(); @@ -352,7 +356,9 @@ namespace Netch.Controllers // ignored } - if (!File.Exists(SystemDriver)) return true; + if (!File.Exists(SystemDriver)) + return true; + NFAPI.nf_unRegisterDriver("netfilter2"); File.Delete(SystemDriver); diff --git a/Netch/Controllers/NTTController.cs b/Netch/Controllers/NTTController.cs index a22fee6d..b32f550b 100644 --- a/Netch/Controllers/NTTController.cs +++ b/Netch/Controllers/NTTController.cs @@ -7,9 +7,15 @@ namespace Netch.Controllers { public class NTTController : Guard, IController { - public override string Name { get; } = "NTT"; public override string MainFile { get; protected set; } = "NTT.exe"; + public override string Name { get; } = "NTT"; + + public override void Stop() + { + StopInstance(); + } + /// /// 启动 NatTypeTester /// @@ -42,6 +48,7 @@ namespace Netch.Controllers var str = line.Split(':').Select(s => s.Trim()).ToArray(); if (str.Length < 2) continue; + var key = str[0]; var value = str[1]; switch (key) @@ -70,6 +77,7 @@ namespace Netch.Controllers if (bindingTest == "Fail") result = "UdpBlocked"; + return (result, localEnd, publicEnd); } catch (Exception e) @@ -87,10 +95,5 @@ namespace Netch.Controllers return (null, null, null); } } - - public override void Stop() - { - StopInstance(); - } } } \ No newline at end of file diff --git a/Netch/Controllers/TUNTAPController.cs b/Netch/Controllers/TUNTAPController.cs index cdbd297f..2aee7182 100644 --- a/Netch/Controllers/TUNTAPController.cs +++ b/Netch/Controllers/TUNTAPController.cs @@ -48,6 +48,7 @@ namespace Netch.Controllers // 查找并安装 TAP 适配器 if (string.IsNullOrEmpty(TUNTAP.GetComponentID())) AddTap(); + SearchTapAdapter(); SetupRouteTable(mode); @@ -102,6 +103,7 @@ namespace Netch.Controllers Task.Run(ClearRouteTable), Task.Run(DNSController.Stop) }; + Task.WaitAll(tasks); } @@ -145,9 +147,7 @@ namespace Netch.Controllers { Logging.Info("代理 → 自定义 DNS"); if (Global.Settings.TUNTAP.UseCustomDNS) - RouteAction(Action.Create, - Global.Settings.TUNTAP.DNS.Select(ip => $"{ip}/32"), - RouteType.TUNTAP); + RouteAction(Action.Create, Global.Settings.TUNTAP.DNS.Select(ip => $"{ip}/32"), RouteType.TUNTAP); else RouteAction(Action.Create, new[] {"1.1.1.1", "8.8.8.8", "9.9.9.9", "185.222.222.222"}.Select(ip => $"{ip}/32"), @@ -160,14 +160,13 @@ namespace Netch.Controllers // 将 TUN/TAP 网卡权重放到最高 Process.Start(new ProcessStartInfo - { - FileName = "netsh", - Arguments = $"interface ip set interface {Global.TUNTAP.Index} metric=0", - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = true, - CreateNoWindow = true - } - ); + { + FileName = "netsh", + Arguments = $"interface ip set interface {Global.TUNTAP.Index} metric=0", + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = true, + CreateNoWindow = true + }); Logging.Info("绕行 → 规则 IP"); RouteAction(Action.Create, mode.FullRule, RouteType.Outbound); @@ -256,8 +255,7 @@ namespace Netch.Controllers return true; } - private void RouteAction(Action action, in IEnumerable ipNetworks, RouteType routeType, - int metric = 0) + private void RouteAction(Action action, in IEnumerable ipNetworks, RouteType routeType, int metric = 0) { foreach (var address in ipNetworks) RouteAction(action, address, routeType, metric); diff --git a/Netch/Controllers/UpdateChecker.cs b/Netch/Controllers/UpdateChecker.cs index d9214e69..dcf2a72d 100644 --- a/Netch/Controllers/UpdateChecker.cs +++ b/Netch/Controllers/UpdateChecker.cs @@ -29,7 +29,9 @@ namespace Netch.Controllers public static Release LatestRelease; public static event EventHandler NewVersionFound; + public static event EventHandler NewVersionFoundFailed; + public static event EventHandler NewVersionNotFound; public static async void Check(bool isPreRelease) diff --git a/Netch/Forms/AboutForm.cs b/Netch/Forms/AboutForm.cs index abf260df..8271c72e 100644 --- a/Netch/Forms/AboutForm.cs +++ b/Netch/Forms/AboutForm.cs @@ -32,4 +32,4 @@ namespace Netch.Forms Process.Start("https://www.mansora.co"); } } -} +} \ No newline at end of file diff --git a/Netch/Forms/GlobalBypassIPForm.cs b/Netch/Forms/GlobalBypassIPForm.cs index a75e450f..934efa69 100644 --- a/Netch/Forms/GlobalBypassIPForm.cs +++ b/Netch/Forms/GlobalBypassIPForm.cs @@ -19,9 +19,8 @@ namespace Netch.Forms IPListBox.Items.AddRange(Global.Settings.BypassIPs.ToArray()); for (var i = 32; i >= 1; i--) - { PrefixComboBox.Items.Add(i); - } + PrefixComboBox.SelectedIndex = 0; } @@ -30,13 +29,9 @@ namespace Netch.Forms if (!string.IsNullOrEmpty(IPTextBox.Text)) { if (IPAddress.TryParse(IPTextBox.Text, out var address)) - { IPListBox.Items.Add(string.Format("{0}/{1}", address, PrefixComboBox.SelectedItem)); - } else - { MessageBoxX.Show(i18N.Translate("Please enter a correct IP address")); - } } else { @@ -47,26 +42,20 @@ namespace Netch.Forms private void DeleteButton_Click(object sender, EventArgs e) { if (IPListBox.SelectedIndex != -1) - { IPListBox.Items.RemoveAt(IPListBox.SelectedIndex); - } else - { MessageBoxX.Show(i18N.Translate("Please select an IP")); - } } private void ControlButton_Click(object sender, EventArgs e) { Global.Settings.BypassIPs.Clear(); foreach (var ip in IPListBox.Items) - { Global.Settings.BypassIPs.Add(ip as string); - } Configuration.Save(); MessageBoxX.Show(i18N.Translate("Saved")); Close(); } } -} +} \ No newline at end of file diff --git a/Netch/Forms/MainForm.cs b/Netch/Forms/MainForm.cs index 9ed6e714..e66a4f4b 100644 --- a/Netch/Forms/MainForm.cs +++ b/Netch/Forms/MainForm.cs @@ -24,6 +24,7 @@ namespace Netch.Forms private bool _comboBoxInitialized; private bool _textRecorded; + public MainForm() { InitializeComponent(); @@ -66,6 +67,7 @@ namespace Netch.Forms Size = new Size(259, 22), Text = i18N.TranslateFormat("Add [{0}] Server", fullName) }; + _mainFormText.Add(control.Name, new[] {"Add [{0}] Server", fullName}); control.Click += AddServerToolStripMenuItem_Click; ServerToolStripMenuItem.DropDownItems.Add(control); @@ -174,20 +176,24 @@ namespace Netch.Forms case Control c: if (_mainFormText.ContainsKey(c.Name)) c.Text = ControlText(c.Name); + break; case ToolStripItem c: if (_mainFormText.ContainsKey(c.Name)) c.Text = ControlText(c.Name); + break; } string ControlText(string name) { var value = _mainFormText[name]; - if (value.Equals(string.Empty)) return string.Empty; + if (value.Equals(string.Empty)) + return string.Empty; if (value is object[] values) return i18N.TranslateFormat(values.First() as string, values.Skip(1).ToArray()); + return i18N.Translate(value); } } @@ -325,6 +331,7 @@ namespace Netch.Forms Remark = "ProxyUpdate", Type = 5 }; + await MainController.Start(ServerComboBox.SelectedItem as Server, mode); proxyServer = $"http://127.0.0.1:{Global.Settings.HTTPLocalPort}"; } @@ -438,6 +445,7 @@ namespace Netch.Forms Remark = "ProxyUpdate", Type = 5 }; + await MainController.Start(ServerComboBox.SelectedItem as Server, mode); } @@ -686,8 +694,7 @@ namespace Netch.Forms public void SelectLastServer() { // 如果值合法,选中该位置 - if (Global.Settings.ServerComboBoxSelectedIndex > 0 && - Global.Settings.ServerComboBoxSelectedIndex < ServerComboBox.Items.Count) + if (Global.Settings.ServerComboBoxSelectedIndex > 0 && Global.Settings.ServerComboBoxSelectedIndex < ServerComboBox.Items.Count) ServerComboBox.SelectedIndex = Global.Settings.ServerComboBoxSelectedIndex; // 如果值非法,且当前 ServerComboBox 中有元素,选择第一个位置 else if (ServerComboBox.Items.Count > 0) @@ -698,7 +705,9 @@ namespace Netch.Forms private void ServerComboBox_SelectedIndexChanged(object sender, EventArgs o) { - if (!_comboBoxInitialized) return; + if (!_comboBoxInitialized) + return; + Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex; } @@ -806,8 +815,7 @@ namespace Netch.Forms public void SelectLastMode() { // 如果值合法,选中该位置 - if (Global.Settings.ModeComboBoxSelectedIndex > 0 && - Global.Settings.ModeComboBoxSelectedIndex < ModeComboBox.Items.Count) + if (Global.Settings.ModeComboBoxSelectedIndex > 0 && Global.Settings.ModeComboBoxSelectedIndex < ModeComboBox.Items.Count) ModeComboBox.SelectedIndex = Global.Settings.ModeComboBoxSelectedIndex; // 如果值非法,且当前 ModeComboBox 中有元素,选择第一个位置 else if (ModeComboBox.Items.Count > 0) @@ -818,7 +826,9 @@ namespace Netch.Forms private void ModeComboBox_SelectedIndexChanged(object sender, EventArgs o) { - if (!_comboBoxInitialized) return; + if (!_comboBoxInitialized) + return; + try { Global.Settings.ModeComboBoxSelectedIndex = Global.Modes.IndexOf((Models.Mode) ModeComboBox.SelectedItem); @@ -906,6 +916,7 @@ namespace Netch.Forms if (Global.Settings.ProfileTableColumnCount == 0) Global.Settings.ProfileTableColumnCount = 5; + var columnCount = Global.Settings.ProfileTableColumnCount; ProfileTable.ColumnCount = profileCount >= columnCount ? columnCount : profileCount; @@ -966,6 +977,7 @@ namespace Netch.Forms Profile profile; if ((profile = Global.Settings.Profiles.SingleOrDefault(p => p.Index == index)) != null) Global.Settings.Profiles.Remove(profile); + profile = new Profile(server, mode, name, index); Global.Settings.Profiles.Add(profile); return profile; @@ -1006,6 +1018,7 @@ namespace Netch.Forms case Keys.Shift: if (profile == null) return; + Global.Settings.Profiles.Remove(profile); profileButton.Tag = null; profileButton.Text = i18N.Translate("None"); @@ -1055,22 +1068,14 @@ namespace Netch.Forms { void StartDisableItems(bool enabled) { - ServerComboBox.Enabled = - ModeComboBox.Enabled = - EditModePictureBox.Enabled = - EditServerPictureBox.Enabled = - DeleteModePictureBox.Enabled = - DeleteServerPictureBox.Enabled = enabled; + ServerComboBox.Enabled = ModeComboBox.Enabled = EditModePictureBox.Enabled = + EditServerPictureBox.Enabled = DeleteModePictureBox.Enabled = DeleteServerPictureBox.Enabled = enabled; // 启动需要禁用的控件 - UninstallServiceToolStripMenuItem.Enabled = - UpdateACLToolStripMenuItem.Enabled = - updateACLWithProxyToolStripMenuItem.Enabled = - updatePACToolStripMenuItem.Enabled = - UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled = - UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Enabled = - UninstallTapDriverToolStripMenuItem.Enabled = - ReloadModesToolStripMenuItem.Enabled = enabled; + UninstallServiceToolStripMenuItem.Enabled = UpdateACLToolStripMenuItem.Enabled = updateACLWithProxyToolStripMenuItem.Enabled = + updatePACToolStripMenuItem.Enabled = UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled = + UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Enabled = UninstallTapDriverToolStripMenuItem.Enabled = + ReloadModesToolStripMenuItem.Enabled = enabled; } _state = value; @@ -1126,6 +1131,7 @@ namespace Netch.Forms { return State == State.Waiting || State == State.Stopped; } + private static bool IsWaiting(State state) { return state == State.Waiting || state == State.Stopped; @@ -1159,6 +1165,7 @@ namespace Netch.Forms if (IsWaiting()) return; + UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = state; } @@ -1243,6 +1250,7 @@ namespace Netch.Forms { if (!MainController.Mode.TestNatRequired()) return; + NttTested = false; Task.Run(() => { @@ -1285,6 +1293,7 @@ namespace Netch.Forms Logging.Info("操作系统即将挂起,自动停止"); ControlButton_Click(null, null); } + break; case PowerModes.Resume: //操作系统即将从挂起状态继续 if (_resumeFlag) @@ -1293,6 +1302,7 @@ namespace Netch.Forms Logging.Info("操作系统即将从挂起状态继续,自动重启"); ControlButton_Click(null, null); } + break; } } @@ -1358,6 +1368,7 @@ namespace Netch.Forms #region FormClosingButton private bool _isFirstCloseWindow = true; + private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { if (e.CloseReason == CloseReason.UserClosing && State != State.Terminating) @@ -1385,6 +1396,7 @@ namespace Netch.Forms NotifyTip($"{i18N.Translate(@"New version available", ": ")}{UpdateChecker.LatestVersionNumber}"); NewVersionLabel.Visible = true; }; + UpdateChecker.Check(Global.Settings.CheckBetaUpdate); } @@ -1398,6 +1410,7 @@ namespace Netch.Forms if (MessageBoxX.Show(i18N.Translate("Download and install now?"), confirm: true) != DialogResult.OK) return; + NotifyTip(i18N.Translate("Start downloading new version")); NewVersionLabel.Enabled = false; @@ -1495,10 +1508,7 @@ namespace Netch.Forms public void NotifyTip(string text, int timeout = 0, bool info = true) { // 会阻塞线程 timeout 秒 - NotifyIcon.ShowBalloonTip(timeout, - UpdateChecker.Name, - text, - info ? ToolTipIcon.Info : ToolTipIcon.Error); + NotifyIcon.ShowBalloonTip(timeout, UpdateChecker.Name, text, info ? ToolTipIcon.Info : ToolTipIcon.Error); } #endregion @@ -1518,7 +1528,8 @@ namespace Netch.Forms // 绘制背景颜色 e.Graphics.FillRectangle(Brushes.White, e.Bounds); - if (e.Index < 0) return; + if (e.Index < 0) + return; // 绘制 备注/名称 字符串 TextRenderer.DrawText(e.Graphics, cbx.Items[e.Index].ToString(), cbx.Font, e.Bounds, Color.Black, TextFormatFlags.Left); @@ -1528,29 +1539,34 @@ namespace Netch.Forms case Server item: { // 计算延迟底色 - var numBoxBackBrush = item.Delay switch - { - > 200 => Brushes.Red, - > 80 => Brushes.Yellow, - >= 0 => _greenBrush, - _ => Brushes.Gray - }; + var numBoxBackBrush = item.Delay switch {> 200 => Brushes.Red, > 80 => Brushes.Yellow, >= 0 => _greenBrush, _ => Brushes.Gray}; // 绘制延迟底色 e.Graphics.FillRectangle(numBoxBackBrush, _numberBoxX, e.Bounds.Y, _numberBoxWidth, e.Bounds.Height); // 绘制延迟字符串 - TextRenderer.DrawText(e.Graphics, item.Delay.ToString(), cbx.Font, new Point(_numberBoxX + _numberBoxWrap, e.Bounds.Y), Color.Black, TextFormatFlags.Left); + TextRenderer.DrawText(e.Graphics, + item.Delay.ToString(), + cbx.Font, + new Point(_numberBoxX + _numberBoxWrap, e.Bounds.Y), + Color.Black, + TextFormatFlags.Left); + break; } case Models.Mode item: { // 绘制 模式Box 底色 - e.Graphics.FillRectangle(Brushes.Gray, _numberBoxX, e.Bounds.Y, _numberBoxWidth, - e.Bounds.Height); + e.Graphics.FillRectangle(Brushes.Gray, _numberBoxX, e.Bounds.Y, _numberBoxWidth, e.Bounds.Height); // 绘制 模式行数 字符串 - TextRenderer.DrawText(e.Graphics, item.Rule.Count.ToString(), cbx.Font, new Point(_numberBoxX + _numberBoxWrap, e.Bounds.Y), Color.Black, TextFormatFlags.Left); + TextRenderer.DrawText(e.Graphics, + item.Rule.Count.ToString(), + cbx.Font, + new Point(_numberBoxX + _numberBoxWrap, e.Bounds.Y), + Color.Black, + TextFormatFlags.Left); + break; } } diff --git a/Netch/Forms/MessageBoxX.cs b/Netch/Forms/MessageBoxX.cs index 2cb31e42..b896d4fc 100644 --- a/Netch/Forms/MessageBoxX.cs +++ b/Netch/Forms/MessageBoxX.cs @@ -14,32 +14,31 @@ namespace Netch.Forms /// 弹窗等级 (标题, 图标) /// 需要确认 /// 阻止 owner Focus() 直到 Messageox 被关闭 - public static DialogResult Show(string text, LogLevel level = LogLevel.INFO, string title = "", bool confirm = false, IWin32Window owner = null) + public static DialogResult Show(string text, + LogLevel level = LogLevel.INFO, + string title = "", + bool confirm = false, + IWin32Window owner = null) { MessageBoxIcon msgIcon; if (string.IsNullOrWhiteSpace(title)) title = level switch - { - LogLevel.INFO => "Information", - LogLevel.WARNING => "Warning", - LogLevel.ERROR => "Error", - _ => throw new ArgumentOutOfRangeException(nameof(level), level, null) - }; + { + LogLevel.INFO => "Information", + LogLevel.WARNING => "Warning", + LogLevel.ERROR => "Error", + _ => throw new ArgumentOutOfRangeException(nameof(level), level, null) + }; msgIcon = level switch - { - LogLevel.INFO => MessageBoxIcon.Information, - LogLevel.WARNING => MessageBoxIcon.Warning, - LogLevel.ERROR => MessageBoxIcon.Exclamation, - _ => throw new ArgumentOutOfRangeException(nameof(level), level, null) - }; + { + LogLevel.INFO => MessageBoxIcon.Information, + LogLevel.WARNING => MessageBoxIcon.Warning, + LogLevel.ERROR => MessageBoxIcon.Exclamation, + _ => throw new ArgumentOutOfRangeException(nameof(level), level, null) + }; - return MessageBox.Show( - owner, - text, - i18N.Translate(title), - confirm ? MessageBoxButtons.OKCancel : MessageBoxButtons.OK, - msgIcon); + return MessageBox.Show(owner, text, i18N.Translate(title), confirm ? MessageBoxButtons.OKCancel : MessageBoxButtons.OK, msgIcon); } } } \ No newline at end of file diff --git a/Netch/Forms/Mode/Process.cs b/Netch/Forms/Mode/Process.cs index c8c68194..d41cb325 100644 --- a/Netch/Forms/Mode/Process.cs +++ b/Netch/Forms/Mode/Process.cs @@ -36,8 +36,7 @@ namespace Netch.Forms.Mode #region 禁用文件名更改 RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged; - FilenameTextBox.Enabled = - UseCustomFilenameBox.Enabled = false; + FilenameTextBox.Enabled = UseCustomFilenameBox.Enabled = false; #endregion @@ -66,7 +65,9 @@ namespace Netch.Forms.Mode { try { - RuleListBox.Items.AddRange(Directory.GetFiles(DirName, "*.exe", SearchOption.AllDirectories).Select(f => Path.GetFileName(f)).ToArray()); + RuleListBox.Items.AddRange(Directory.GetFiles(DirName, "*.exe", SearchOption.AllDirectories) + .Select(f => Path.GetFileName(f)) + .ToArray()); } catch (Exception) { @@ -88,13 +89,16 @@ namespace Netch.Forms.Mode RuleListBox.SelectedIndex = RuleListBox.IndexFromPoint(e.X, e.Y); if (RuleListBox.SelectedIndex == -1) return; + if (e.Button == MouseButtons.Right) contextMenuStrip.Show(RuleListBox, e.Location); } private void deleteRule_Click(object sender, EventArgs e) { - if (RuleListBox.SelectedIndex == -1) return; + if (RuleListBox.SelectedIndex == -1) + return; + RuleListBox.Items.RemoveAt(RuleListBox.SelectedIndex); Edited = true; } @@ -137,6 +141,7 @@ namespace Netch.Forms.Mode EnsurePathExists = true, NavigateToShortcut = true }; + if (dialog.ShowDialog(Win32Native.GetForegroundWindow()) == CommonFileDialogResult.Ok) { ScanDirectory(dialog.FileName); @@ -191,6 +196,7 @@ namespace Netch.Forms.Mode Type = 0, Remark = RemarkTextBox.Text }; + mode.Rule.AddRange(RuleListBox.Items.Cast()); mode.WriteFile(); diff --git a/Netch/Forms/ServerForm.cs b/Netch/Forms/ServerForm.cs index b230fc03..b58f3770 100644 --- a/Netch/Forms/ServerForm.cs +++ b/Netch/Forms/ServerForm.cs @@ -1,6 +1,6 @@ -using System.ComponentModel; -using System; +using System; using System.Collections.Generic; +using System.ComponentModel; using System.Drawing; using System.Linq; using System.Windows.Forms; @@ -10,18 +10,28 @@ using Netch.Utils; namespace Netch.Forms { - [DesignerCategory(@"Code")] public abstract class ServerForm : Form { - protected abstract string TypeName { get; } - protected Server Server { get; set; } - - private int _controlLines = 2; - private const int ControlLineHeight = 28; private const int InputBoxWidth = 294; + private readonly Dictionary> _checkActions = new(); + + private readonly Dictionary> _saveActions = new(); + + private int _controlLines = 2; + private Label AddressLabel; + protected TextBox AddressTextBox; + + private readonly IContainer components = null; + + private GroupBox ConfigurationGroupBox; + private Label PortLabel; + private TextBox PortTextBox; + private Label RemarkLabel; + protected TextBox RemarkTextBox; + protected ServerForm() { InitializeComponent(); @@ -36,6 +46,10 @@ namespace Netch.Forms _saveActions.Add(PortTextBox, s => Server.Port = ushort.Parse((string) s)); } + protected abstract string TypeName { get; } + + protected Server Server { get; set; } + public new void ShowDialog() { AfterFactor(); @@ -65,7 +79,12 @@ namespace Netch.Forms PerformLayout(); } - protected void CreateTextBox(string name, string remark, Func check, Action save, string value, int width = InputBoxWidth) + protected void CreateTextBox(string name, + string remark, + Func check, + Action save, + string value, + int width = InputBoxWidth) { _controlLines++; @@ -77,22 +96,21 @@ namespace Netch.Forms TextAlign = HorizontalAlignment.Center, Text = value }; + _checkActions.Add(textBox, check); _saveActions.Add(textBox, o => save.Invoke((string) o)); - ConfigurationGroupBox.Controls.AddRange( - new Control[] + ConfigurationGroupBox.Controls.AddRange(new Control[] + { + textBox, + new Label { - textBox, - new Label - { - AutoSize = true, - Location = new Point(10, ControlLineHeight * _controlLines), - Name = $"{name}Label", - Size = new Size(56, 17), - Text = remark - } + AutoSize = true, + Location = new Point(10, ControlLineHeight * _controlLines), + Name = $"{name}Label", + Size = new Size(56, 17), + Text = remark } - ); + }); } protected void CreateComboBox(string name, string remark, List values, Action save, string value, int width = InputBoxWidth) @@ -108,24 +126,23 @@ namespace Netch.Forms DropDownStyle = ComboBoxStyle.DropDownList, FormattingEnabled = true }; + comboBox.Items.AddRange(values.ToArray()); comboBox.SelectedIndex = values.IndexOf(value); comboBox.DrawItem += Utils.Utils.DrawCenterComboBox; _saveActions.Add(comboBox, o => save.Invoke((string) o)); - ConfigurationGroupBox.Controls.AddRange( - new Control[] + ConfigurationGroupBox.Controls.AddRange(new Control[] + { + comboBox, + new Label { - comboBox, - new Label - { - AutoSize = true, - Location = new Point(10, ControlLineHeight * _controlLines), - Name = $"{name}Label", - Size = new Size(56, 17), - Text = remark - } + AutoSize = true, + Location = new Point(10, ControlLineHeight * _controlLines), + Name = $"{name}Label", + Size = new Size(56, 17), + Text = remark } - ); + }); } protected void CreateCheckBox(string name, string remark, Action save, bool value) @@ -140,19 +157,14 @@ namespace Netch.Forms Checked = value, Text = remark }; + _saveActions.Add(checkBox, o => save.Invoke((bool) o)); - ConfigurationGroupBox.Controls.AddRange( - new Control[] - { - checkBox - } - ); + ConfigurationGroupBox.Controls.AddRange(new Control[] + { + checkBox + }); } - private readonly Dictionary> _checkActions = new Dictionary>(); - - private readonly Dictionary> _saveActions = new Dictionary>(); - private void AddSaveButton() { _controlLines++; @@ -164,6 +176,7 @@ namespace Netch.Forms Text = "Save", UseVisualStyleBackColor = true }; + control.Click += ControlButton_Click; ConfigurationGroupBox.Controls.Add(control); } @@ -180,12 +193,9 @@ namespace Netch.Forms } if (!flag) - { return; - } foreach (var pair in _saveActions) - { switch (pair.Key) { case CheckBox c: @@ -195,7 +205,6 @@ namespace Netch.Forms pair.Value.Invoke(pair.Key.Text); break; } - } if (Global.Settings.Server.IndexOf(Server) == -1) Global.Settings.Server.Add(Server); @@ -205,14 +214,10 @@ namespace Netch.Forms Close(); } - private IContainer components = null; - protected override void Dispose(bool disposing) { if (disposing) - { components?.Dispose(); - } base.Dispose(disposing); } @@ -306,7 +311,7 @@ namespace Netch.Forms AutoSizeMode = AutoSizeMode.GrowAndShrink; ClientSize = new Size(444, 137); Controls.Add(ConfigurationGroupBox); - Font = new Font("微软雅黑", 9F, FontStyle.Regular, GraphicsUnit.Point, (byte) 134); + Font = new Font("微软雅黑", 9F, FontStyle.Regular, GraphicsUnit.Point, 134); FormBorderStyle = FormBorderStyle.FixedSingle; Icon = Icon.FromHandle(Resources.Netch.GetHicon()); Margin = new Padding(3, 4, 3, 4); @@ -315,13 +320,5 @@ namespace Netch.Forms Padding = new Padding(11, 5, 11, 4); StartPosition = FormStartPosition.CenterScreen; } - - private GroupBox ConfigurationGroupBox; - private Label RemarkLabel; - protected TextBox RemarkTextBox; - private Label PortLabel; - protected TextBox AddressTextBox; - private TextBox PortTextBox; - private Label AddressLabel; } } \ No newline at end of file diff --git a/Netch/Forms/SettingForm.cs b/Netch/Forms/SettingForm.cs index c9082fee..1ecc4373 100644 --- a/Netch/Forms/SettingForm.cs +++ b/Netch/Forms/SettingForm.cs @@ -15,6 +15,7 @@ namespace Netch.Forms private readonly Dictionary> _checkActions = new(); private readonly Dictionary> _saveActions = new(); + public SettingForm() { InitializeComponent(); @@ -36,46 +37,34 @@ namespace Netch.Forms p => p.ToString() != HTTPPortTextBox.Text && p.ToString() != RedirectorTextBox.Text, p => Global.Settings.Socks5LocalPort = p, Global.Settings.Socks5LocalPort); + BindTextBox(HTTPPortTextBox, p => p.ToString() != Socks5PortTextBox.Text && p.ToString() != RedirectorTextBox.Text, p => Global.Settings.HTTPLocalPort = p, Global.Settings.HTTPLocalPort); + BindTextBox(RedirectorTextBox, p => p.ToString() != Socks5PortTextBox.Text && p.ToString() != HTTPPortTextBox.Text, p => Global.Settings.RedirectorTCPPort = p, Global.Settings.RedirectorTCPPort); + BindCheckBox(AllowDevicesCheckBox, c => Global.Settings.LocalAddress = AllowDevicesCheckBox.Checked ? "0.0.0.0" : "127.0.0.1", - Global.Settings.LocalAddress switch - { - "127.0.0.1" => false, - "0.0.0.0" => true, - _ => false - }); + Global.Settings.LocalAddress switch {"127.0.0.1" => false, "0.0.0.0" => true, _ => false}); - BindCheckBox(BootShadowsocksFromDLLCheckBox, - c => Global.Settings.BootShadowsocksFromDLL = c, - Global.Settings.BootShadowsocksFromDLL); - BindCheckBox(ResolveServerHostnameCheckBox, - c => Global.Settings.ResolveServerHostname = c, - Global.Settings.ResolveServerHostname); + BindCheckBox(BootShadowsocksFromDLLCheckBox, c => Global.Settings.BootShadowsocksFromDLL = c, Global.Settings.BootShadowsocksFromDLL); + BindCheckBox(ResolveServerHostnameCheckBox, c => Global.Settings.ResolveServerHostname = c, Global.Settings.ResolveServerHostname); - BindRadioBox(ICMPingRadioBtn, - _ => { }, - !Global.Settings.ServerTCPing); + BindRadioBox(ICMPingRadioBtn, _ => { }, !Global.Settings.ServerTCPing); - BindRadioBox(TCPingRadioBtn, - c => Global.Settings.ServerTCPing = c, - Global.Settings.ServerTCPing); + BindRadioBox(TCPingRadioBtn, c => Global.Settings.ServerTCPing = c, Global.Settings.ServerTCPing); - BindTextBox(ProfileCountTextBox, - i => i > -1, - i => Global.Settings.ProfileCount = i, - Global.Settings.ProfileCount); + BindTextBox(ProfileCountTextBox, i => i > -1, i => Global.Settings.ProfileCount = i, Global.Settings.ProfileCount); BindTextBox(DetectionTickTextBox, i => ServerHelper.DelayTestHelper.Range.InRange(i), i => Global.Settings.DetectionTick = i, Global.Settings.DetectionTick); + BindTextBox(StartedPingIntervalTextBox, _ => true, i => Global.Settings.StartedPingInterval = i, @@ -83,10 +72,7 @@ namespace Netch.Forms InitSTUN(); - BindTextBox(AclAddrTextBox, - s => true, - s => Global.Settings.ACL = s, - Global.Settings.ACL); + BindTextBox(AclAddrTextBox, s => true, s => Global.Settings.ACL = s, Global.Settings.ACL); AclAddrTextBox.Text = Global.Settings.ACL; LanguageComboBox.Items.AddRange(i18N.GetTranslateList().ToArray()); @@ -96,26 +82,15 @@ namespace Netch.Forms #region Process Mode - BindCheckBox(ModifySystemDNSCheckBox, - b => Global.Settings.ModifySystemDNS = b, - Global.Settings.ModifySystemDNS); + BindCheckBox(ModifySystemDNSCheckBox, b => Global.Settings.ModifySystemDNS = b, Global.Settings.ModifySystemDNS); - BindTextBox(ModifiedDNSTextBox, - s => DNS.TrySplit(s, out _, 2), - s => Global.Settings.ModifiedDNS = s, - Global.Settings.ModifiedDNS); + BindTextBox(ModifiedDNSTextBox, s => DNS.TrySplit(s, out _, 2), s => Global.Settings.ModifiedDNS = s, Global.Settings.ModifiedDNS); - BindCheckBox(RedirectorSSCheckBox, - s => Global.Settings.RedirectorSS = s, - Global.Settings.RedirectorSS); + BindCheckBox(RedirectorSSCheckBox, s => Global.Settings.RedirectorSS = s, Global.Settings.RedirectorSS); - BindCheckBox(NoProxyForUdpCheckBox, - s => Global.Settings.ProcessNoProxyForUdp = s, - Global.Settings.ProcessNoProxyForUdp); + BindCheckBox(NoProxyForUdpCheckBox, s => Global.Settings.ProcessNoProxyForUdp = s, Global.Settings.ProcessNoProxyForUdp); - BindCheckBox(NoProxyForTcpCheckBox, - s => Global.Settings.ProcessNoProxyForTcp = s, - Global.Settings.ProcessNoProxyForTcp); + BindCheckBox(NoProxyForTcpCheckBox, s => Global.Settings.ProcessNoProxyForTcp = s, Global.Settings.ProcessNoProxyForTcp); #endregion @@ -125,17 +100,18 @@ namespace Netch.Forms s => IPAddress.TryParse(s, out _), s => Global.Settings.TUNTAP.Address = s, Global.Settings.TUNTAP.Address); + BindTextBox(TUNTAPNetmaskTextBox, s => IPAddress.TryParse(s, out _), s => Global.Settings.TUNTAP.Netmask = s, Global.Settings.TUNTAP.Netmask); + BindTextBox(TUNTAPGatewayTextBox, s => IPAddress.TryParse(s, out _), s => Global.Settings.TUNTAP.Gateway = s, Global.Settings.TUNTAP.Gateway); - BindCheckBox(UseCustomDNSCheckBox, - b => { Global.Settings.TUNTAP.UseCustomDNS = b; }, - Global.Settings.TUNTAP.UseCustomDNS); + + BindCheckBox(UseCustomDNSCheckBox, b => { Global.Settings.TUNTAP.UseCustomDNS = b; }, Global.Settings.TUNTAP.UseCustomDNS); BindTextBox(TUNTAPDNSTextBox, s => !UseCustomDNSCheckBox.Checked || DNS.TrySplit(s, out _, 2), @@ -146,52 +122,40 @@ namespace Netch.Forms }, DNS.Join(Global.Settings.TUNTAP.DNS)); - BindCheckBox(ProxyDNSCheckBox, - b => Global.Settings.TUNTAP.ProxyDNS = b, - Global.Settings.TUNTAP.ProxyDNS); - BindCheckBox(UseFakeDNSCheckBox, - b => Global.Settings.TUNTAP.UseFakeDNS = b, - Global.Settings.TUNTAP.UseFakeDNS); + BindCheckBox(ProxyDNSCheckBox, b => Global.Settings.TUNTAP.ProxyDNS = b, Global.Settings.TUNTAP.ProxyDNS); + BindCheckBox(UseFakeDNSCheckBox, b => Global.Settings.TUNTAP.UseFakeDNS = b, Global.Settings.TUNTAP.UseFakeDNS); #endregion #region V2Ray - BindCheckBox(XrayConeCheckBox, - b => Global.Settings.V2RayConfig.XrayCone = b, - Global.Settings.V2RayConfig.XrayCone); + BindCheckBox(XrayConeCheckBox, b => Global.Settings.V2RayConfig.XrayCone = b, Global.Settings.V2RayConfig.XrayCone); - BindCheckBox(TLSAllowInsecureCheckBox, - b => Global.Settings.V2RayConfig.AllowInsecure = b, - Global.Settings.V2RayConfig.AllowInsecure); - BindCheckBox(UseMuxCheckBox, - b => Global.Settings.V2RayConfig.UseMux = b, - Global.Settings.V2RayConfig.UseMux); + BindCheckBox(TLSAllowInsecureCheckBox, b => Global.Settings.V2RayConfig.AllowInsecure = b, Global.Settings.V2RayConfig.AllowInsecure); + BindCheckBox(UseMuxCheckBox, b => Global.Settings.V2RayConfig.UseMux = b, Global.Settings.V2RayConfig.UseMux); - BindTextBox(mtuTextBox, - i => true, - i => Global.Settings.V2RayConfig.KcpConfig.mtu = i, - Global.Settings.V2RayConfig.KcpConfig.mtu); - BindTextBox(ttiTextBox, - i => true, - i => Global.Settings.V2RayConfig.KcpConfig.tti = i, - Global.Settings.V2RayConfig.KcpConfig.tti); + BindTextBox(mtuTextBox, i => true, i => Global.Settings.V2RayConfig.KcpConfig.mtu = i, Global.Settings.V2RayConfig.KcpConfig.mtu); + BindTextBox(ttiTextBox, i => true, i => Global.Settings.V2RayConfig.KcpConfig.tti = i, Global.Settings.V2RayConfig.KcpConfig.tti); BindTextBox(uplinkCapacityTextBox, i => true, i => Global.Settings.V2RayConfig.KcpConfig.uplinkCapacity = i, Global.Settings.V2RayConfig.KcpConfig.uplinkCapacity); + BindTextBox(downlinkCapacityTextBox, i => true, i => Global.Settings.V2RayConfig.KcpConfig.downlinkCapacity = i, Global.Settings.V2RayConfig.KcpConfig.downlinkCapacity); + BindTextBox(readBufferSizeTextBox, i => true, i => Global.Settings.V2RayConfig.KcpConfig.readBufferSize = i, Global.Settings.V2RayConfig.KcpConfig.readBufferSize); + BindTextBox(writeBufferSizeTextBox, i => true, i => Global.Settings.V2RayConfig.KcpConfig.writeBufferSize = i, Global.Settings.V2RayConfig.KcpConfig.writeBufferSize); + BindCheckBox(congestionCheckBox, b => Global.Settings.V2RayConfig.KcpConfig.congestion = b, Global.Settings.V2RayConfig.KcpConfig.congestion); @@ -200,46 +164,27 @@ namespace Netch.Forms #region Others - BindCheckBox(ExitWhenClosedCheckBox, - b => Global.Settings.ExitWhenClosed = b, - Global.Settings.ExitWhenClosed); + BindCheckBox(ExitWhenClosedCheckBox, b => Global.Settings.ExitWhenClosed = b, Global.Settings.ExitWhenClosed); - BindCheckBox(StopWhenExitedCheckBox, - b => Global.Settings.StopWhenExited = b, - Global.Settings.StopWhenExited); + BindCheckBox(StopWhenExitedCheckBox, b => Global.Settings.StopWhenExited = b, Global.Settings.StopWhenExited); - BindCheckBox(StartWhenOpenedCheckBox, - b => Global.Settings.StartWhenOpened = b, - Global.Settings.StartWhenOpened); + BindCheckBox(StartWhenOpenedCheckBox, b => Global.Settings.StartWhenOpened = b, Global.Settings.StartWhenOpened); - BindCheckBox(MinimizeWhenStartedCheckBox, - b => Global.Settings.MinimizeWhenStarted = b, - Global.Settings.MinimizeWhenStarted); + BindCheckBox(MinimizeWhenStartedCheckBox, b => Global.Settings.MinimizeWhenStarted = b, Global.Settings.MinimizeWhenStarted); - BindCheckBox(RunAtStartupCheckBox, - b => Global.Settings.RunAtStartup = b, - Global.Settings.RunAtStartup); + BindCheckBox(RunAtStartupCheckBox, b => Global.Settings.RunAtStartup = b, Global.Settings.RunAtStartup); - BindCheckBox(CheckUpdateWhenOpenedCheckBox, - b => Global.Settings.CheckUpdateWhenOpened = b, - Global.Settings.CheckUpdateWhenOpened); + BindCheckBox(CheckUpdateWhenOpenedCheckBox, b => Global.Settings.CheckUpdateWhenOpened = b, Global.Settings.CheckUpdateWhenOpened); - BindCheckBox(CheckBetaUpdateCheckBox, - b => Global.Settings.CheckBetaUpdate = b, - Global.Settings.CheckBetaUpdate); + BindCheckBox(CheckBetaUpdateCheckBox, b => Global.Settings.CheckBetaUpdate = b, Global.Settings.CheckBetaUpdate); - BindCheckBox(UpdateServersWhenOpenedCheckBox, - b => Global.Settings.UpdateServersWhenOpened = b, - Global.Settings.UpdateServersWhenOpened); + BindCheckBox(UpdateServersWhenOpenedCheckBox, b => Global.Settings.UpdateServersWhenOpened = b, Global.Settings.UpdateServersWhenOpened); #endregion #region AioDNS - BindTextBox(AioDNSRulePathTextBox, - s => true, - s => Global.Settings.AioDNS.RulePath = s, - Global.Settings.AioDNS.RulePath); + BindTextBox(AioDNSRulePathTextBox, s => true, s => Global.Settings.AioDNS.RulePath = s, Global.Settings.AioDNS.RulePath); BindTextBox(ChinaDNSTextBox, s => IPAddress.TryParse(s, out _), @@ -257,9 +202,7 @@ namespace Netch.Forms private void TUNTAPUseCustomDNSCheckBox_CheckedChanged(object sender, EventArgs e) { if (UseCustomDNSCheckBox.Checked) - TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.DNS.Any() - ? DNS.Join(Global.Settings.TUNTAP.DNS) - : "1.1.1.1"; + TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.DNS.Any() ? DNS.Join(Global.Settings.TUNTAP.DNS) : "1.1.1.1"; else TUNTAPDNSTextBox.Text = "AioDNS"; } @@ -358,17 +301,19 @@ namespace Netch.Forms private void BindTextBox(TextBox control, Func check, Action save, object value) { control.Text = value.ToString(); - _checkActions.Add(control, s => - { - try + _checkActions.Add(control, + s => { - return check.Invoke((T) Convert.ChangeType(s, typeof(T))); - } - catch - { - return false; - } - }); + try + { + return check.Invoke((T) Convert.ChangeType(s, typeof(T))); + } + catch + { + return false; + } + }); + _saveActions.Add(control, c => save.Invoke((T) Convert.ChangeType(((TextBox) c).Text, typeof(T)))); } @@ -377,6 +322,7 @@ namespace Netch.Forms control.Checked = value; _saveActions.Add(control, c => save.Invoke(((CheckBox) c).Checked)); } + private void BindRadioBox(RadioButton control, Action save, bool value) { control.Checked = value; @@ -385,12 +331,14 @@ namespace Netch.Forms private void NoProxyForUdpCheckBox_CheckedChanged(object sender, EventArgs e) { - if (NoProxyForUdpCheckBox.Checked) NoProxyForTcpCheckBox.Checked = false; + if (NoProxyForUdpCheckBox.Checked) + NoProxyForTcpCheckBox.Checked = false; } private void NoProxyForTcpCheckBox_CheckedChanged(object sender, EventArgs e) { - if (NoProxyForTcpCheckBox.Checked) NoProxyForUdpCheckBox.Checked = false; + if (NoProxyForTcpCheckBox.Checked) + NoProxyForUdpCheckBox.Checked = false; } } } \ No newline at end of file diff --git a/Netch/Forms/SubscribeForm.cs b/Netch/Forms/SubscribeForm.cs index 5a927f67..9ce1fe95 100644 --- a/Netch/Forms/SubscribeForm.cs +++ b/Netch/Forms/SubscribeForm.cs @@ -83,7 +83,8 @@ namespace Netch.Forms return; } - if (!LinkTextBox.Text.StartsWith("HTTP://", StringComparison.OrdinalIgnoreCase) && !LinkTextBox.Text.StartsWith("HTTPS://", StringComparison.OrdinalIgnoreCase)) + if (!LinkTextBox.Text.StartsWith("HTTP://", StringComparison.OrdinalIgnoreCase) && + !LinkTextBox.Text.StartsWith("HTTPS://", StringComparison.OrdinalIgnoreCase)) { MessageBoxX.Show(i18N.Translate("Link must start with http:// or https://")); return; @@ -124,7 +125,8 @@ namespace Netch.Forms private void DeleteToolStripMenuItem_Click(object sender, EventArgs e) { - if (MessageBoxX.Show(i18N.Translate("Delete or not ? Will clean up the corresponding group of items in the server list"), confirm: true) != DialogResult.OK) + if (MessageBoxX.Show(i18N.Translate("Delete or not ? Will clean up the corresponding group of items in the server list"), + confirm: true) != DialogResult.OK) return; var subscribeLink = Global.Settings.SubscribeLink[SelectedIndex]; @@ -188,6 +190,7 @@ namespace Netch.Forms LinkTextBox.Text = string.Empty; UserAgentTextBox.Text = WebUtil.DefaultUserAgent; } + private void SetEditingGroup(int index) { if (index == -1) diff --git a/Netch/Global.cs b/Netch/Global.cs index 794774d9..91265315 100644 --- a/Netch/Global.cs +++ b/Netch/Global.cs @@ -20,6 +20,9 @@ namespace Netch /// public const string EOF = "\r\n"; + public const string UserACL = "data\\user.acl"; + public const string BuiltinACL = "bin\\default.acl"; + public static readonly string NetchDir = Application.StartupPath; public static readonly string NetchExecutable = Application.ExecutablePath; @@ -31,74 +34,74 @@ namespace Netch public static readonly Mutex Mutex = new(false, "Global\\Netch"); - public static class Flags - { - public static bool SupportFakeDns => _supportFakeDns ??= new TUNTAPController().TestFakeDNS(); - public static readonly bool IsWindows10Upper = Environment.OSVersion.Version.Major >= 10; - - private static bool? _supportFakeDns; - } - - public const string UserACL = "data\\user.acl"; - public const string BuiltinACL = "bin\\default.acl"; - - /// - /// 出口适配器 - /// - public static class Outbound - { - /// - /// 索引 - /// - public static int Index = -1; - - /// - /// 地址 - /// - public static IPAddress Address => Adapter.GetIPProperties().UnicastAddresses.First(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork).Address; - - /// - /// 网关 - /// - public static IPAddress Gateway; - - public static NetworkInterface Adapter; - } - - /// - /// TUN/TAP 适配器 - /// - public static class TUNTAP - { - /// - /// 适配器 - /// - public static NetworkInterface Adapter; - - /// - /// 索引 - /// - public static int Index = -1; - - /// - /// 组件 ID - /// - public static string ComponentID = string.Empty; - } - /// /// 用于读取和写入的配置 /// - public static Setting Settings = new Setting(); + public static Setting Settings = new(); /// /// 用于存储模式 /// - public static readonly List Modes = new List(); + public static readonly List Modes = new(); /// - /// Windows Job API + /// Windows Job API /// - public static readonly JobObject Job = new JobObject(); + public static readonly JobObject Job = new(); + + public static class Flags + { + public static readonly bool IsWindows10Upper = Environment.OSVersion.Version.Major >= 10; + + private static bool? _supportFakeDns; + + public static bool SupportFakeDns => _supportFakeDns ??= new TUNTAPController().TestFakeDNS(); + } + + /// + /// 出口适配器 + /// + public static class Outbound + { + /// + /// 索引 + /// + public static int Index = -1; + + /// + /// 网关 + /// + public static IPAddress Gateway; + + public static NetworkInterface Adapter; + + /// + /// 地址 + /// + public static IPAddress Address => Adapter.GetIPProperties() + .UnicastAddresses.First(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork) + .Address; + } + + /// + /// TUN/TAP 适配器 + /// + public static class TUNTAP + { + /// + /// 适配器 + /// + public static NetworkInterface Adapter; + + /// + /// 索引 + /// + public static int Index = -1; + + /// + /// 组件 ID + /// + public static string ComponentID = string.Empty; + } } } \ No newline at end of file diff --git a/Netch/Models/GitHubRelease/Asset.cs b/Netch/Models/GitHubRelease/Asset.cs index f763388f..a1e2f2b1 100644 --- a/Netch/Models/GitHubRelease/Asset.cs +++ b/Netch/Models/GitHubRelease/Asset.cs @@ -5,17 +5,29 @@ namespace Netch.Models.GitHubRelease public class Asset { public string url { get; set; } + public int id { get; set; } + public string node_id { get; set; } + public string name { get; set; } + public object label { get; set; } + public GitHubUser uploader { get; set; } + public string content_type { get; set; } + public string state { get; set; } + public int size { get; set; } + public int download_count { get; set; } + public DateTime created_at { get; set; } + public DateTime updated_at { get; set; } + public string browser_download_url { get; set; } } } \ No newline at end of file diff --git a/Netch/Models/GitHubRelease/GitHubRelease.cs b/Netch/Models/GitHubRelease/GitHubRelease.cs index dae74350..c408f7a3 100644 --- a/Netch/Models/GitHubRelease/GitHubRelease.cs +++ b/Netch/Models/GitHubRelease/GitHubRelease.cs @@ -5,12 +5,12 @@ private readonly string _owner; private readonly string _repo; - public string AllReleaseUrl => $@"https://api.github.com/repos/{_owner}/{_repo}/releases"; - public GitHubRelease(string owner, string repo) { _owner = owner; _repo = repo; } + + public string AllReleaseUrl => $@"https://api.github.com/repos/{_owner}/{_repo}/releases"; } -} +} \ No newline at end of file diff --git a/Netch/Models/GitHubRelease/GitHubUser.cs b/Netch/Models/GitHubRelease/GitHubUser.cs index bae538f7..7964ca77 100644 --- a/Netch/Models/GitHubRelease/GitHubUser.cs +++ b/Netch/Models/GitHubRelease/GitHubUser.cs @@ -3,22 +3,39 @@ public class GitHubUser { public string login { get; set; } + public int id { get; set; } + public string node_id { get; set; } + public string avatar_url { get; set; } + public string gravatar_id { get; set; } + public string url { get; set; } + public string html_url { get; set; } + public string followers_url { get; set; } + public string following_url { get; set; } + public string gists_url { get; set; } + public string starred_url { get; set; } + public string subscriptions_url { get; set; } + public string organizations_url { get; set; } + public string repos_url { get; set; } + public string events_url { get; set; } + public string received_events_url { get; set; } + public string type { get; set; } + public bool site_admin { get; set; } } } \ No newline at end of file diff --git a/Netch/Models/GitHubRelease/Release.cs b/Netch/Models/GitHubRelease/Release.cs index 9579e2d9..8c8b0324 100644 --- a/Netch/Models/GitHubRelease/Release.cs +++ b/Netch/Models/GitHubRelease/Release.cs @@ -5,22 +5,39 @@ namespace Netch.Models.GitHubRelease public class Release { public string url { get; set; } + public string assets_url { get; set; } + public string upload_url { get; set; } + public string html_url { get; set; } + public int id { get; set; } + public string node_id { get; set; } + public string tag_name { get; set; } + public string target_commitish { get; set; } + public string name { get; set; } + public bool draft { get; set; } + public GitHubUser author { get; set; } + public bool prerelease { get; set; } + public DateTime created_at { get; set; } + public DateTime published_at { get; set; } + public Asset[] assets { get; set; } + public string tarball_url { get; set; } + public string zipball_url { get; set; } + public string body { get; set; } } } \ No newline at end of file diff --git a/Netch/Models/GitHubRelease/SuffixVersion.cs b/Netch/Models/GitHubRelease/SuffixVersion.cs index ecf201ef..ae778b32 100644 --- a/Netch/Models/GitHubRelease/SuffixVersion.cs +++ b/Netch/Models/GitHubRelease/SuffixVersion.cs @@ -7,9 +7,13 @@ namespace Netch.Models.GitHubRelease public struct SuffixVersion : ICloneable, IComparable, IComparable, IEquatable { public int Major { get; } + public int Minor { get; } + public int Patch { get; } + public string PreRelease { get; } + public int Build { get; } public SuffixVersion(int major, int minor, int patch, string preRelease, int build) @@ -39,16 +43,10 @@ namespace Netch.Models.GitHubRelease if (splitStr.Length > 1) foreach (var c in splitStr[1]) - { if (int.TryParse(c.ToString(), out var n)) - { build = build * 10 + n; - } else - { preRelease.Append(c); - } - } return new SuffixVersion(dotNetVersion, preRelease.ToString(), build); } @@ -67,41 +65,49 @@ namespace Netch.Models.GitHubRelease } } - - public object Clone() => new SuffixVersion(Major, Major, Patch, PreRelease, Build); + public object Clone() + { + return new SuffixVersion(Major, Major, Patch, PreRelease, Build); + } public int CompareTo(object obj) { if (obj is SuffixVersion version) return CompareTo(version); + return -1; } /// - /// /// /// /// - /// greater than 0 newer + /// greater than 0 newer /// public int CompareTo(SuffixVersion other) { var majorComparison = Major.CompareTo(other.Major); if (majorComparison != 0) return majorComparison; + var minorComparison = Minor.CompareTo(other.Minor); if (minorComparison != 0) return minorComparison; + var patchComparison = Patch.CompareTo(other.Patch); if (patchComparison != 0) return patchComparison; + if (PreRelease == string.Empty) return other.PreRelease == string.Empty ? 0 : 1; + if (other.PreRelease == string.Empty) return -1; + var suffixComparison = string.Compare(PreRelease, other.PreRelease, StringComparison.Ordinal); if (suffixComparison != 0) return suffixComparison; + return Build.CompareTo(other.Build); } @@ -115,7 +121,6 @@ namespace Netch.Models.GitHubRelease return obj is SuffixVersion other && Equals(other); } - public override int GetHashCode() { unchecked diff --git a/Netch/Models/GitHubRelease/VersionComparer.cs b/Netch/Models/GitHubRelease/VersionComparer.cs index 7e384af3..744f404f 100644 --- a/Netch/Models/GitHubRelease/VersionComparer.cs +++ b/Netch/Models/GitHubRelease/VersionComparer.cs @@ -9,4 +9,4 @@ namespace Netch.Models.GitHubRelease return VersionUtil.CompareVersion(x?.ToString(), y?.ToString()); } } -} +} \ No newline at end of file diff --git a/Netch/Models/GitHubRelease/VersionUtil.cs b/Netch/Models/GitHubRelease/VersionUtil.cs index 3a283553..3d5ba3dc 100644 --- a/Netch/Models/GitHubRelease/VersionUtil.cs +++ b/Netch/Models/GitHubRelease/VersionUtil.cs @@ -8,9 +8,7 @@ namespace Netch.Models.GitHubRelease public static Release GetLatestRelease(IEnumerable releases, bool isPreRelease) { if (!isPreRelease) - { releases = releases.Where(release => !release.prerelease); - } releases = releases.Where(release => IsVersionString(release.tag_name)); var ordered = releases.OrderByDescending(release => release.tag_name, new VersionComparer()); diff --git a/Netch/Models/Mode.cs b/Netch/Models/Mode.cs index 0abdc103..f2193e5d 100644 --- a/Netch/Models/Mode.cs +++ b/Netch/Models/Mode.cs @@ -41,10 +41,12 @@ namespace Netch.Models /// /// public int Type = 0; + public Mode(string fullName) { FullName = fullName; } + public Mode() { } @@ -63,6 +65,7 @@ namespace Netch.Models { if (string.IsNullOrWhiteSpace(s)) continue; + if (s.StartsWith("//")) continue; @@ -136,6 +139,7 @@ namespace Netch.Models return $"# {Remark}, {Type}, {(BypassChina ? 1 : 0)}{Global.EOF}{string.Join(Global.EOF, Rule)}"; } } + public static class ModeExtension { /// 是否会转发 UDP diff --git a/Netch/Models/Profile.cs b/Netch/Models/Profile.cs index 4398c947..109188d6 100644 --- a/Netch/Models/Profile.cs +++ b/Netch/Models/Profile.cs @@ -2,12 +2,12 @@ { public class Profile { - public string ServerRemark; + public int Index; public string ModeRemark; public string ProfileName; - public int Index; + public string ServerRemark; - public Profile(Server server, Mode mode, string name,int index) + public Profile(Server server, Mode mode, string name, int index) { ServerRemark = server.Remark; ModeRemark = mode.Remark; @@ -16,12 +16,10 @@ } /// - /// Return a dummy one. + /// Return a dummy one. /// public Profile() { } - - } -} +} \ No newline at end of file diff --git a/Netch/Models/Server.cs b/Netch/Models/Server.cs index 647c2b74..95542b0e 100644 --- a/Netch/Models/Server.cs +++ b/Netch/Models/Server.cs @@ -79,7 +79,9 @@ namespace Netch.Models { try { - return Global.Settings.ServerTCPing ? await Utils.Utils.TCPingAsync(destination, Port) : Utils.Utils.ICMPing(destination, Port); + return Global.Settings.ServerTCPing + ? await Utils.Utils.TCPingAsync(destination, Port) + : Utils.Utils.ICMPing(destination, Port); } catch (Exception) { diff --git a/Netch/Models/State.cs b/Netch/Models/State.cs index 7a0cf49a..8861a916 100644 --- a/Netch/Models/State.cs +++ b/Netch/Models/State.cs @@ -30,7 +30,6 @@ /// Stopped, - /// /// 退出中 /// @@ -43,6 +42,7 @@ { if (state == State.Waiting) return "Waiting for command"; + return state.ToString(); } } diff --git a/Netch/Models/SubscribeLink.cs b/Netch/Models/SubscribeLink.cs index 1789cb30..eae203e5 100644 --- a/Netch/Models/SubscribeLink.cs +++ b/Netch/Models/SubscribeLink.cs @@ -7,16 +7,16 @@ /// public bool Enable = true; - /// - /// 备注 - /// - public string Remark; - /// /// 链接 /// public string Link; + /// + /// 备注 + /// + public string Remark; + /// /// User Agent /// diff --git a/Netch/NativeMethods.cs b/Netch/NativeMethods.cs index 4dd0b886..dace1e94 100644 --- a/Netch/NativeMethods.cs +++ b/Netch/NativeMethods.cs @@ -5,7 +5,7 @@ namespace Netch public static class NativeMethods { /// - /// 创建路由规则 + /// 创建路由规则 /// /// 目标地址 /// CIDR @@ -17,7 +17,7 @@ namespace Netch public static extern bool CreateRoute(string address, int cidr, string gateway, int index, int metric = 0); /// - /// 删除路由规则 + /// 删除路由规则 /// /// 目标地址 /// 掩码地址 diff --git a/Netch/Netch.cs b/Netch/Netch.cs index 840ece01..1d60a0c5 100644 --- a/Netch/Netch.cs +++ b/Netch/Netch.cs @@ -24,7 +24,9 @@ namespace Netch // 设置当前目录 Directory.SetCurrentDirectory(Global.NetchDir); - Environment.SetEnvironmentVariable("PATH", Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) + ";" + Path.Combine(Global.NetchDir, "bin"), EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("PATH", + Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) + ";" + Path.Combine(Global.NetchDir, "bin"), + EnvironmentVariableTarget.Process); Updater.Updater.CleanOld(); diff --git a/Netch/Servers/Shadowsocks/Form/ShadowsocksForm.cs b/Netch/Servers/Shadowsocks/Form/ShadowsocksForm.cs index 7a0f4baf..068135a2 100644 --- a/Netch/Servers/Shadowsocks/Form/ShadowsocksForm.cs +++ b/Netch/Servers/Shadowsocks/Form/ShadowsocksForm.cs @@ -4,28 +4,16 @@ namespace Netch.Servers.Shadowsocks.Form { public class ShadowsocksForm : ServerForm { - protected override string TypeName { get; } = "Shadowsocks"; - public ShadowsocksForm(Shadowsocks server = default) { server ??= new Shadowsocks(); Server = server; - CreateTextBox("Password", "Password", - s => true, - s => server.Password = s, - server.Password); - CreateComboBox("EncryptMethod", "Encrypt Method", - SSGlobal.EncryptMethods, - s => server.EncryptMethod = s, - server.EncryptMethod); - CreateTextBox("Plugin", "Plugin", - s => true, - s => server.Plugin = s, - server.Plugin); - CreateTextBox("PluginsOption", "Plugin Options", - s => true, - s => server.PluginOption = s, - server.PluginOption); + CreateTextBox("Password", "Password", s => true, s => server.Password = s, server.Password); + CreateComboBox("EncryptMethod", "Encrypt Method", SSGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod); + CreateTextBox("Plugin", "Plugin", s => true, s => server.Plugin = s, server.Plugin); + CreateTextBox("PluginsOption", "Plugin Options", s => true, s => server.PluginOption = s, server.PluginOption); } + + protected override string TypeName { get; } = "Shadowsocks"; } } \ No newline at end of file diff --git a/Netch/Servers/Shadowsocks/Models/SSD/Main.cs b/Netch/Servers/Shadowsocks/Models/SSD/Main.cs index cfa1c16a..6ae0e586 100644 --- a/Netch/Servers/Shadowsocks/Models/SSD/Main.cs +++ b/Netch/Servers/Shadowsocks/Models/SSD/Main.cs @@ -9,11 +9,6 @@ namespace Netch.Servers.Shadowsocks.Models.SSD /// public string airport; - /// - /// 端口 - /// - public ushort port; - /// /// 加密方式 /// @@ -34,6 +29,11 @@ namespace Netch.Servers.Shadowsocks.Models.SSD /// public string plugin_options; + /// + /// 端口 + /// + public ushort port; + /// /// 服务器数组 /// diff --git a/Netch/Servers/Shadowsocks/Models/SSD/SSDServer.cs b/Netch/Servers/Shadowsocks/Models/SSD/SSDServer.cs index ed246edb..80d2cbb3 100644 --- a/Netch/Servers/Shadowsocks/Models/SSD/SSDServer.cs +++ b/Netch/Servers/Shadowsocks/Models/SSD/SSDServer.cs @@ -2,16 +2,6 @@ { public class SSDServer { - /// - /// 服务器地址 - /// - public string server; - - /// - /// 端口 - /// - public ushort port; - /// /// 加密方式 /// @@ -32,9 +22,18 @@ /// public string plugin_options; + /// + /// 端口 + /// + public ushort port; + /// /// 备注 /// public string remarks; + /// + /// 服务器地址 + /// + public string server; } } \ No newline at end of file diff --git a/Netch/Servers/Shadowsocks/Models/ShadowsocksConfig.cs b/Netch/Servers/Shadowsocks/Models/ShadowsocksConfig.cs index b28f19bb..753f1e1a 100644 --- a/Netch/Servers/Shadowsocks/Models/ShadowsocksConfig.cs +++ b/Netch/Servers/Shadowsocks/Models/ShadowsocksConfig.cs @@ -3,11 +3,17 @@ namespace Netch.Servers.Shadowsocks.Models public class ShadowsocksConfig { public string server { get; set; } + public ushort server_port { get; set; } + public string password { get; set; } + public string method { get; set; } + public string remarks { get; set; } + public string plugin { get; set; } + public string plugin_opts { get; set; } } } \ No newline at end of file diff --git a/Netch/Servers/Shadowsocks/SSController.cs b/Netch/Servers/Shadowsocks/SSController.cs index 63307201..1af767da 100644 --- a/Netch/Servers/Shadowsocks/SSController.cs +++ b/Netch/Servers/Shadowsocks/SSController.cs @@ -11,14 +11,17 @@ namespace Netch.Servers.Shadowsocks public class SSController : Guard, IServerController { public bool DllFlag; + public override string MainFile { get; protected set; } = "Shadowsocks.exe"; protected override IEnumerable StartedKeywords { get; } = new[] {"listening at"}; protected override IEnumerable StoppedKeywords { get; } = new[] {"Invalid config path", "usage", "plugin service exit unexpectedly"}; + public override string Name { get; } = "Shadowsocks"; public ushort? Socks5LocalPort { get; set; } + public string LocalAddress { get; set; } public void Start(in Server s, in Mode mode) @@ -57,17 +60,12 @@ namespace Netch.Servers.Shadowsocks #region Argument var argument = new StringBuilder(); - argument.Append( - $"-s {server.AutoResolveHostname()} " + - $"-p {server.Port} " + - $"-b {this.LocalAddress()} " + - $"-l {this.Socks5LocalPort()} " + - $"-m {server.EncryptMethod} " + - $"-k \"{server.Password}\" " + - "-u"); + argument.Append($"-s {server.AutoResolveHostname()} " + $"-p {server.Port} " + $"-b {this.LocalAddress()} " + + $"-l {this.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}\""); + argument.Append($" --plugin {server.Plugin}" + $" --plugin-opts \"{server.PluginOption}\""); + if (mode.BypassChina) argument.Append($" --acl {Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}"); diff --git a/Netch/Servers/Shadowsocks/SSUtil.cs b/Netch/Servers/Shadowsocks/SSUtil.cs index 1fb3410b..541f6258 100644 --- a/Netch/Servers/Shadowsocks/SSUtil.cs +++ b/Netch/Servers/Shadowsocks/SSUtil.cs @@ -16,9 +16,13 @@ namespace Netch.Servers.Shadowsocks public class SSUtil : IServerUtil { public ushort Priority { get; } = 1; + public string TypeName { get; } = "SS"; + public string FullName { get; } = "Shadowsocks"; + public string ShortName { get; } = "SS"; + public string[] UriScheme { get; } = {"ss", "ssd"}; public Server ParseJObject(in JObject j) @@ -40,7 +44,8 @@ namespace Netch.Servers.Shadowsocks { 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); + return "ss://" + ShareLink.URLSafeBase64Encode($"{server.EncryptMethod}:{server.Password}@{server.Hostname}:{server.Port}") + "#" + + HttpUtility.UrlEncode(server.Remark); } public IServerController GetController() @@ -51,18 +56,28 @@ namespace Netch.Servers.Shadowsocks public IEnumerable ParseUri(string text) { if (text.StartsWith("ss://")) - { return new[] {ParseSsUri(text)}; - } if (text.StartsWith("ssd://")) - { return ParseSsdUri(text); - } 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; + } + public IEnumerable ParseSsdUri(string s) { var json = JsonConvert.DeserializeObject
(ShareLink.URLSafeBase64Decode(s.Substring(6))); @@ -75,7 +90,9 @@ namespace Netch.Servers.Shadowsocks 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 + PluginOption = string.IsNullOrEmpty(json.plugin_options) + ? string.IsNullOrEmpty(server.plugin_options) ? null : server.plugin_options + : json.plugin_options }) .Where(CheckServer); } @@ -112,11 +129,13 @@ namespace Netch.Servers.Shadowsocks 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; } @@ -137,14 +156,16 @@ namespace Netch.Servers.Shadowsocks var finder = new Regex(@"^ss://(?.+?)@(?.+):(?\d+)"); var parser = new Regex(@"^(?.+?):(?.+)$"); var match = finder.Match(text); - if (!match.Success) throw new FormatException(); + if (!match.Success) + throw new FormatException(); data.Hostname = match.Groups["server"].Value; data.Port = ushort.Parse(match.Groups["port"].Value); var base64 = ShareLink.URLSafeBase64Decode(match.Groups["base64"].Value); match = parser.Match(base64); - if (!match.Success) throw new FormatException(); + if (!match.Success) + throw new FormatException(); data.EncryptMethod = match.Groups["method"].Value; data.Password = match.Groups["password"].Value; @@ -153,7 +174,8 @@ namespace Netch.Servers.Shadowsocks { var parser = new Regex(@"^((?.+?):(?.+)@(?.+):(?\d+))"); var match = parser.Match(ShareLink.URLSafeBase64Decode(text.Replace("ss://", ""))); - if (!match.Success) throw new FormatException(); + if (!match.Success) + throw new FormatException(); data.Hostname = match.Groups["server"].Value; data.Port = ushort.Parse(match.Groups["port"].Value); @@ -168,19 +190,5 @@ namespace Netch.Servers.Shadowsocks 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/Servers/Shadowsocks/Shadowsocks.cs b/Netch/Servers/Shadowsocks/Shadowsocks.cs index 469c56c4..f95af97a 100644 --- a/Netch/Servers/Shadowsocks/Shadowsocks.cs +++ b/Netch/Servers/Shadowsocks/Shadowsocks.cs @@ -5,6 +5,11 @@ namespace Netch.Servers.Shadowsocks { public class Shadowsocks : Server { + public Shadowsocks() + { + Type = "SS"; + } + /// /// 加密方式 /// @@ -25,12 +30,10 @@ namespace Netch.Servers.Shadowsocks /// public string PluginOption { get; set; } - public Shadowsocks() + public bool HasPlugin() { - Type = "SS"; + return !string.IsNullOrWhiteSpace(Plugin) && !string.IsNullOrWhiteSpace(PluginOption); } - - public bool HasPlugin() => !string.IsNullOrWhiteSpace(Plugin) && !string.IsNullOrWhiteSpace(PluginOption); } public static class SSGlobal @@ -38,7 +41,7 @@ namespace Netch.Servers.Shadowsocks /// /// SS 加密列表 /// - public static readonly List EncryptMethods = new List + public static readonly List EncryptMethods = new() { "rc4-md5", "aes-128-gcm", diff --git a/Netch/Servers/ShadowsocksR/Form/ShadowsocksRForm.cs b/Netch/Servers/ShadowsocksR/Form/ShadowsocksRForm.cs index 5f1a9553..5a855ada 100644 --- a/Netch/Servers/ShadowsocksR/Form/ShadowsocksRForm.cs +++ b/Netch/Servers/ShadowsocksR/Form/ShadowsocksRForm.cs @@ -4,36 +4,18 @@ namespace Netch.Servers.ShadowsocksR.Form { public class ShadowsocksRForm : ServerForm { - protected override string TypeName { get; } = "ShadowsocksR"; - public ShadowsocksRForm(ShadowsocksR server = default) { server ??= new ShadowsocksR(); Server = server; - CreateTextBox("Password", "Password", - s => true, - s => server.Password = s, - server.Password); - CreateComboBox("EncryptMethod", "Encrypt Method", - SSRGlobal.EncryptMethods, - s => server.EncryptMethod = s, - server.EncryptMethod); - CreateComboBox("Protocol", "Protocol", - SSRGlobal.Protocols, - s => server.Protocol = s, - server.Protocol); - CreateTextBox("ProtocolParam", "Protocol Param", - s => true, - s => server.ProtocolParam = s, - server.ProtocolParam); - CreateComboBox("OBFS", "OBFS", - SSRGlobal.OBFSs, - s => server.OBFS = s, - server.OBFS); - CreateTextBox("OBFSParam", "OBFS Param", - s => true, - s => server.OBFSParam = s, - server.OBFSParam); + CreateTextBox("Password", "Password", s => true, s => server.Password = s, server.Password); + CreateComboBox("EncryptMethod", "Encrypt Method", SSRGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod); + CreateComboBox("Protocol", "Protocol", SSRGlobal.Protocols, s => server.Protocol = s, server.Protocol); + CreateTextBox("ProtocolParam", "Protocol Param", s => true, s => server.ProtocolParam = s, server.ProtocolParam); + CreateComboBox("OBFS", "OBFS", SSRGlobal.OBFSs, s => server.OBFS = s, server.OBFS); + CreateTextBox("OBFSParam", "OBFS Param", s => true, s => server.OBFSParam = s, server.OBFSParam); } + + protected override string TypeName { get; } = "ShadowsocksR"; } } \ No newline at end of file diff --git a/Netch/Servers/ShadowsocksR/SSRController.cs b/Netch/Servers/ShadowsocksR/SSRController.cs index 71f027cd..054566be 100644 --- a/Netch/Servers/ShadowsocksR/SSRController.cs +++ b/Netch/Servers/ShadowsocksR/SSRController.cs @@ -17,6 +17,7 @@ namespace Netch.Servers.ShadowsocksR public override string Name { get; } = "ShadowsocksR"; public ushort? Socks5LocalPort { get; set; } + public string LocalAddress { get; set; } public void Start(in Server s, in Mode mode) @@ -30,13 +31,15 @@ namespace Netch.Servers.ShadowsocksR if (!string.IsNullOrEmpty(server.Protocol)) { argument.Append($" -O {server.Protocol}"); - if (!string.IsNullOrEmpty(server.ProtocolParam)) argument.Append($" -G \"{server.ProtocolParam}\""); + if (!string.IsNullOrEmpty(server.ProtocolParam)) + argument.Append($" -G \"{server.ProtocolParam}\""); } if (!string.IsNullOrEmpty(server.OBFS)) { argument.Append($" -o {server.OBFS}"); - if (!string.IsNullOrEmpty(server.OBFSParam)) argument.Append($" -g \"{server.OBFSParam}\""); + if (!string.IsNullOrEmpty(server.OBFSParam)) + argument.Append($" -g \"{server.OBFSParam}\""); } argument.Append($" -b {this.LocalAddress()} -l {this.Socks5LocalPort()} -u"); diff --git a/Netch/Servers/ShadowsocksR/SSRUtil.cs b/Netch/Servers/ShadowsocksR/SSRUtil.cs index 91a18d9c..6bd40fcb 100644 --- a/Netch/Servers/ShadowsocksR/SSRUtil.cs +++ b/Netch/Servers/ShadowsocksR/SSRUtil.cs @@ -13,9 +13,13 @@ namespace Netch.Servers.ShadowsocksR public class SSRUtil : IServerUtil { public ushort Priority { get; } = 1; + public string TypeName { get; } = "SSR"; + public string FullName { get; } = "ShadowsocksR"; + public string ShortName { get; } = "SR"; + public string[] UriScheme { get; } = {"ssr"}; public Server ParseJObject(in JObject j) @@ -23,7 +27,6 @@ namespace Netch.Servers.ShadowsocksR return j.ToObject(); } - public void Edit(Server s) { new ShadowsocksRForm((ShadowsocksR) s).ShowDialog(); @@ -40,8 +43,12 @@ namespace Netch.Servers.ShadowsocksR // 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}"); + 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 IServerController GetController() @@ -51,7 +58,8 @@ namespace Netch.Servers.ShadowsocksR /// /// SSR链接解析器 - /// Copy From https://github.com/HMBSbige/ShadowsocksR-Windows/blob/d9dc8d032a6e04c14b9dc6c8f673c9cc5aa9f607/shadowsocks-csharp/Model/Server.cs#L428 + /// Copy From + /// https://github.com/HMBSbige/ShadowsocksR-Windows/blob/d9dc8d032a6e04c14b9dc6c8f673c9cc5aa9f607/shadowsocks-csharp/Model/Server.cs#L428 /// Thx :D /// /// @@ -73,7 +81,8 @@ namespace Netch.Servers.ShadowsocksR data = data.Substring(0, paramStartPos); } - if (data.IndexOf("/", StringComparison.Ordinal) >= 0) data = data.Substring(0, data.LastIndexOf("/", StringComparison.Ordinal)); + if (data.IndexOf("/", StringComparison.Ordinal) >= 0) + data = data.Substring(0, data.LastIndexOf("/", StringComparison.Ordinal)); var urlFinder = new Regex("^(.+):([^:]+):([^:]*):([^:]+):([^:]*):([^:]+)"); var match = urlFinder.Match(data); @@ -93,16 +102,18 @@ namespace Netch.Servers.ShadowsocksR var obfsParam = ""; var remarks = ""; - if (paramsDict.ContainsKey("protoparam")) protocolParam = ShareLink.URLSafeBase64Decode(paramsDict["protoparam"]); + if (paramsDict.ContainsKey("protoparam")) + protocolParam = ShareLink.URLSafeBase64Decode(paramsDict["protoparam"]); - if (paramsDict.ContainsKey("obfsparam")) obfsParam = ShareLink.URLSafeBase64Decode(paramsDict["obfsparam"]); + if (paramsDict.ContainsKey("obfsparam")) + obfsParam = ShareLink.URLSafeBase64Decode(paramsDict["obfsparam"]); - if (paramsDict.ContainsKey("remarks")) remarks = ShareLink.URLSafeBase64Decode(paramsDict["remarks"]); + if (paramsDict.ContainsKey("remarks")) + remarks = ShareLink.URLSafeBase64Decode(paramsDict["remarks"]); var group = paramsDict.ContainsKey("group") ? ShareLink.URLSafeBase64Decode(paramsDict["group"]) : string.Empty; if (SSGlobal.EncryptMethods.Contains(method) && protocol == "origin" && obfs == "plain") - { return new[] { new Shadowsocks.Shadowsocks @@ -115,7 +126,6 @@ namespace Netch.Servers.ShadowsocksR Group = group } }; - } return new[] { diff --git a/Netch/Servers/ShadowsocksR/ShadowsocksR.cs b/Netch/Servers/ShadowsocksR/ShadowsocksR.cs index 5fae408f..a91c73e8 100644 --- a/Netch/Servers/ShadowsocksR/ShadowsocksR.cs +++ b/Netch/Servers/ShadowsocksR/ShadowsocksR.cs @@ -5,6 +5,11 @@ namespace Netch.Servers.ShadowsocksR { public class ShadowsocksR : Server { + public ShadowsocksR() + { + Type = "SSR"; + } + /// /// 加密方式 /// @@ -34,11 +39,6 @@ namespace Netch.Servers.ShadowsocksR /// 协议参数 /// public string ProtocolParam { get; set; } - - public ShadowsocksR() - { - Type = "SSR"; - } } public class SSRGlobal @@ -46,7 +46,7 @@ namespace Netch.Servers.ShadowsocksR /// /// SSR 协议列表 /// - public static readonly List Protocols = new List + public static readonly List Protocols = new() { "origin", "verify_deflate", @@ -59,7 +59,7 @@ namespace Netch.Servers.ShadowsocksR /// /// SSR 混淆列表 /// - public static readonly List OBFSs = new List + public static readonly List OBFSs = new() { "plain", "http_simple", @@ -70,7 +70,7 @@ namespace Netch.Servers.ShadowsocksR /// /// SS/SSR 加密方式 /// - public static readonly List EncryptMethods = new List + public static readonly List EncryptMethods = new() { "none", "table", diff --git a/Netch/Servers/Socks5/Form/Socks5Form.cs b/Netch/Servers/Socks5/Form/Socks5Form.cs index 7cb6a00c..46fb7e97 100644 --- a/Netch/Servers/Socks5/Form/Socks5Form.cs +++ b/Netch/Servers/Socks5/Form/Socks5Form.cs @@ -4,20 +4,14 @@ namespace Netch.Servers.Socks5.Form { public class Socks5Form : ServerForm { - protected override string TypeName { get; } = "Socks5"; - public Socks5Form(Socks5 server = default) { server ??= new Socks5(); Server = server; - CreateTextBox("Username", "Username", - s => true, - s => server.Username = s, - server.Username); - CreateTextBox("Password", "Password", - s => true, - s => server.Password = s, - server.Password); + CreateTextBox("Username", "Username", s => true, s => server.Username = s, server.Username); + CreateTextBox("Password", "Password", s => true, s => server.Password = s, server.Password); } + + protected override string TypeName { get; } = "Socks5"; } } \ No newline at end of file diff --git a/Netch/Servers/Socks5/S5Util.cs b/Netch/Servers/Socks5/S5Util.cs index 96f7b61b..9302ccc2 100644 --- a/Netch/Servers/Socks5/S5Util.cs +++ b/Netch/Servers/Socks5/S5Util.cs @@ -10,9 +10,13 @@ namespace Netch.Servers.Socks5 public class S5Util : IServerUtil { public ushort Priority { get; } = 0; + public string TypeName { get; } = "Socks5"; + public string FullName { get; } = "Socks5"; + public string ShortName { get; } = "S5"; + public string[] UriScheme { get; } = { }; public Server ParseJObject(in JObject j) @@ -46,17 +50,14 @@ namespace Netch.Servers.Socks5 public IEnumerable ParseUri(string text) { - var dict = text - .Replace("tg://socks?", "") + 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 { @@ -65,14 +66,10 @@ namespace Netch.Servers.Socks5 }; 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}; } diff --git a/Netch/Servers/Socks5/Socks5.cs b/Netch/Servers/Socks5/Socks5.cs index 65c01c2a..d051cca5 100644 --- a/Netch/Servers/Socks5/Socks5.cs +++ b/Netch/Servers/Socks5/Socks5.cs @@ -19,6 +19,9 @@ namespace Netch.Servers.Socks5 Type = "Socks5"; } - public bool Auth() => !string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password); + public bool Auth() + { + return !string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password); + } } } \ No newline at end of file diff --git a/Netch/Servers/Trojan/Form/TrojanForm.cs b/Netch/Servers/Trojan/Form/TrojanForm.cs index 83467952..c2a4ec07 100644 --- a/Netch/Servers/Trojan/Form/TrojanForm.cs +++ b/Netch/Servers/Trojan/Form/TrojanForm.cs @@ -4,20 +4,14 @@ namespace Netch.Servers.Trojan.Form { public class TrojanForm : ServerForm { - protected override string TypeName { get; } = "Trojan"; - public TrojanForm(Trojan server = default) { server ??= new Trojan(); Server = server; - CreateTextBox("Password", "Password", - s => true, - s => server.Password = s, - server.Password); - CreateTextBox("Host", "Host", - s => true, - s => server.Host = s, - server.Host); + CreateTextBox("Password", "Password", s => true, s => server.Password = s, server.Password); + CreateTextBox("Host", "Host", s => true, s => server.Host = s, server.Host); } + + protected override string TypeName { get; } = "Trojan"; } } \ No newline at end of file diff --git a/Netch/Servers/Trojan/Models/TrojanConfig.cs b/Netch/Servers/Trojan/Models/TrojanConfig.cs index a10cc779..c725b20b 100644 --- a/Netch/Servers/Trojan/Models/TrojanConfig.cs +++ b/Netch/Servers/Trojan/Models/TrojanConfig.cs @@ -4,11 +4,6 @@ namespace Netch.Servers.Trojan.Models { public class TrojanConfig { - /// - /// 启动类型 - /// - public string run_type = "client"; - /// /// 监听地址 /// @@ -19,6 +14,16 @@ namespace Netch.Servers.Trojan.Models /// public int local_port = 2801; + /// + /// 日志级别 + /// + public int log_level = 1; + + /// + /// 密码 + /// + public List password; + /// /// 远端地址 /// @@ -28,47 +33,41 @@ namespace Netch.Servers.Trojan.Models /// 远端端口 /// public int remote_port; - /// - /// 密码 + /// 启动类型 /// - public List password; + public string run_type = "client"; - /// - /// 日志级别 - /// - public int log_level = 1; - - public TrojanSSL ssl = new TrojanSSL(); - public TrojanTCP tcp = new TrojanTCP(); + public TrojanSSL ssl = new(); + public TrojanTCP tcp = new(); } public class TrojanSSL { - public bool verify = false; - public bool verify_hostname = false; - public string cert; - 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 + public List alpn = new() { "h2", "http/1.1" }; + public string cert; + 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 curves = ""; public bool reuse_session = true; public bool session_ticket = true; - public string curves = ""; + public string sni = string.Empty; + public bool verify = false; + public bool verify_hostname = false; } public class TrojanTCP { - public bool no_delay = false; - public bool keep_alive = true; - public bool reuse_port = false; public bool fast_open = true; public int fast_open_qlen = 20; + public bool keep_alive = true; + public bool no_delay = false; + public bool reuse_port = false; } } \ No newline at end of file diff --git a/Netch/Servers/Trojan/TrojanController.cs b/Netch/Servers/Trojan/TrojanController.cs index 3e574f71..9eca8a31 100644 --- a/Netch/Servers/Trojan/TrojanController.cs +++ b/Netch/Servers/Trojan/TrojanController.cs @@ -10,11 +10,15 @@ namespace Netch.Servers.Trojan public class TrojanController : Guard, IServerController { public override string MainFile { get; protected set; } = "Trojan.exe"; + protected override IEnumerable StartedKeywords { get; } = new[] {"started"}; + protected override IEnumerable StoppedKeywords { get; } = new[] {"exiting"}; + public override string Name { get; } = "Trojan"; public ushort? Socks5LocalPort { get; set; } + public string LocalAddress { get; set; } public void Start(in Server s, in Mode mode) @@ -35,10 +39,14 @@ namespace Netch.Servers.Trojan if (!string.IsNullOrWhiteSpace(server.Host)) trojanConfig.ssl.sni = server.Host; - File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(trojanConfig, Formatting.Indented, new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore - })); + File.WriteAllText("data\\last.json", + JsonConvert.SerializeObject(trojanConfig, + Formatting.Indented, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + })); + StartInstanceAuto("-c ..\\data\\last.json"); } diff --git a/Netch/Servers/Trojan/TrojanUtil.cs b/Netch/Servers/Trojan/TrojanUtil.cs index b750c008..3eb8c15d 100644 --- a/Netch/Servers/Trojan/TrojanUtil.cs +++ b/Netch/Servers/Trojan/TrojanUtil.cs @@ -12,9 +12,13 @@ namespace Netch.Servers.Trojan public class TrojanUtil : IServerUtil { public ushort Priority { get; } = 3; + public string TypeName { get; } = "Trojan"; + public string FullName { get; } = "Trojan"; + public string ShortName { get; } = "TR"; + public string[] UriScheme { get; } = {"trojan"}; public Server ParseJObject(in JObject j) @@ -79,9 +83,7 @@ namespace Netch.Servers.Trojan 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; diff --git a/Netch/Servers/V2ray/Models/V2rayConfig.cs b/Netch/Servers/V2ray/Models/V2rayConfig.cs index be39e1ea..fd7b1329 100644 --- a/Netch/Servers/V2ray/Models/V2rayConfig.cs +++ b/Netch/Servers/V2ray/Models/V2rayConfig.cs @@ -207,24 +207,22 @@ namespace Netch.Servers.V2ray.Models public class TCPRequest { - public string version = "1.1"; + public TCPRequestHeaders headers; public string method = "GET"; public string path = "/"; - - public TCPRequestHeaders headers; + public string version = "1.1"; } public class TCPRequestHeaders { - public string Host; - //public string User_Agent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"; public string Accept_Encoding = "gzip, deflate"; public string Connection = "keep-alive"; + public string Host; public string Pragma = "no-cache"; } diff --git a/Netch/Servers/V2ray/Models/V2rayNSharing.cs b/Netch/Servers/V2ray/Models/V2rayNSharing.cs index 7c969933..e5b15f0b 100644 --- a/Netch/Servers/V2ray/Models/V2rayNSharing.cs +++ b/Netch/Servers/V2ray/Models/V2rayNSharing.cs @@ -5,59 +5,58 @@ /// public class V2rayNSharing { - /// - /// 链接版本 - /// - public string v = string.Empty; - - /// - /// 备注 - /// - public string ps = string.Empty; - /// /// 地址 /// public string add = string.Empty; - /// - /// 端口 - /// - public string port = string.Empty; - - /// - /// 用户 ID - /// - public string id = string.Empty; - /// /// 额外 ID /// public string aid = string.Empty; - /// - /// 传输协议 - /// - public string net = string.Empty; - - /// - /// 伪装类型 - /// - public string type = string.Empty; - /// /// 伪装域名(HTTP,WS) /// public string host = string.Empty; + /// + /// 用户 ID + /// + public string id = string.Empty; + + /// + /// 传输协议 + /// + public string net = string.Empty; + /// /// 伪装路径 /// public string path = string.Empty; + /// + /// 端口 + /// + public string port = string.Empty; + + /// + /// 备注 + /// + public string ps = string.Empty; + /// /// 是否使用 TLS /// public string tls = string.Empty; + + /// + /// 伪装类型 + /// + public string type = string.Empty; + /// + /// 链接版本 + /// + public string v = string.Empty; } } \ No newline at end of file diff --git a/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs b/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs index 448b1edb..2d0d40f9 100644 --- a/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs +++ b/Netch/Servers/V2ray/Utils/V2rayConfigUtils.cs @@ -21,7 +21,9 @@ namespace Netch.Servers.V2ray.Utils outbound(server, mode, ref v2rayConfig); - return JsonConvert.SerializeObject(v2rayConfig, Formatting.Indented, new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}); + return JsonConvert.SerializeObject(v2rayConfig, + Formatting.Indented, + new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}); } catch { @@ -87,6 +89,7 @@ namespace Netch.Servers.V2ray.Utils directRuleObject.domain.Add("geosite:cn"); else directRuleObject.ip.Add("geoip:cn"); + break; default: directRuleObject.domain.Add("geosite:cn"); @@ -115,6 +118,7 @@ namespace Netch.Servers.V2ray.Utils if (CheckRuleItem(ref directRuleObject)) v2rayConfig.routing.rules.Add(directRuleObject); + if (CheckRuleItem(ref blockRuleObject)) v2rayConfig.routing.rules.Add(blockRuleObject); } @@ -175,6 +179,7 @@ namespace Netch.Servers.V2ray.Utils address = server.AutoResolveHostname(), port = server.Port }; + outbound.settings.vnext = new List {vnextItem}; var usersItem = new UsersItem @@ -184,6 +189,7 @@ namespace Netch.Servers.V2ray.Utils flow = string.Empty, encryption = vless.EncryptMethod }; + vnextItem.users.Add(usersItem); var streamSettings = outbound.streamSettings; @@ -214,6 +220,7 @@ namespace Netch.Servers.V2ray.Utils address = server.AutoResolveHostname(), port = server.Port }; + outbound.settings.vnext = new List {vnextItem}; var usersItem = new UsersItem @@ -222,6 +229,7 @@ namespace Netch.Servers.V2ray.Utils alterId = vmess.AlterID, security = vmess.EncryptMethod }; + vnextItem.users.Add(usersItem); var streamSettings = outbound.streamSettings; @@ -302,9 +310,7 @@ namespace Netch.Servers.V2ray.Utils case "ws": var wsSettings = new WsSettings { - headers = !string.IsNullOrWhiteSpace(server.Host) - ? new Headers {Host = server.Host} - : null, + headers = !string.IsNullOrWhiteSpace(server.Host) ? new Headers {Host = server.Host} : null, path = !string.IsNullOrWhiteSpace(server.Path) ? server.Path : null }; diff --git a/Netch/Servers/V2ray/V2rayController.cs b/Netch/Servers/V2ray/V2rayController.cs index e42f60b4..16733fd7 100644 --- a/Netch/Servers/V2ray/V2rayController.cs +++ b/Netch/Servers/V2ray/V2rayController.cs @@ -17,7 +17,9 @@ namespace Netch.Servers.V2ray public override string Name { get; } = "Xray"; public ushort? Socks5LocalPort { get; set; } + public string LocalAddress { get; set; } + public virtual void Start(in Server s, in Mode mode) { File.WriteAllText("data\\last.json", V2rayConfigUtils.GenerateClientConfig(s, mode)); diff --git a/Netch/Servers/V2ray/V2rayUtils.cs b/Netch/Servers/V2ray/V2rayUtils.cs index 364e41f1..b8ca6c6e 100644 --- a/Netch/Servers/V2ray/V2rayUtils.cs +++ b/Netch/Servers/V2ray/V2rayUtils.cs @@ -50,6 +50,7 @@ namespace Netch.Servers.V2ray server.FakeType = parameter.Get("headerType") ?? "none"; break; } + server.TLSSecureType = parameter.Get("security") ?? "none"; if (server.TLSSecureType != "none") { @@ -58,6 +59,7 @@ namespace Netch.Servers.V2ray ((VLESS.VLESS) server).Flow = parameter.Get("flow") ?? ""; } } + var finder = new Regex(@$"^{scheme}://(?.+?)@(?.+):(?\d+)"); var match = finder.Match(text.Split('?')[0]); if (!match.Success) @@ -78,6 +80,7 @@ namespace Netch.Servers.V2ray return null; } } + public static string GetVShareLink(Server s, string scheme = "vmess") { var server = (VMess.VMess) s; @@ -87,6 +90,7 @@ namespace Netch.Servers.V2ray if (server.EncryptMethod == "none") // VLESS outbounds[].settings.encryption,当前可选值只有 none parameter.Add("encryption", server.EncryptMethod); + // transport-specific fields switch (server.TransferProtocol) { @@ -95,18 +99,22 @@ namespace Netch.Servers.V2ray case "kcp": if (server.FakeType != "none") parameter.Add("headerType", server.FakeType); + if (!server.Path.IsNullOrWhiteSpace()) parameter.Add("seed", Uri.EscapeDataString(server.Path)); + break; case "ws": parameter.Add("path", Uri.EscapeDataString(server.Path.IsNullOrWhiteSpace() ? "/" : server.Path)); if (!server.Host.IsNullOrWhiteSpace()) parameter.Add("host", server.Host); + break; case "h2": parameter.Add("path", Uri.EscapeDataString(server.Path.IsNullOrWhiteSpace() ? "/" : server.Path)); if (!server.Host.IsNullOrWhiteSpace()) parameter.Add("host", Uri.EscapeDataString(server.Host)); + break; case "quic": if (server.QUICSecure != "none") @@ -117,6 +125,7 @@ namespace Netch.Servers.V2ray if (server.FakeType != "none") parameter.Add("headerType", server.FakeType); + break; } @@ -135,7 +144,8 @@ namespace Netch.Servers.V2ray } } - return $"{scheme}://{server.UserID}@{server.Hostname}:{server.Port}?{string.Join("&", parameter.Select(p => $"{p.Key}={p.Value}"))}{(server.Remark.IsNullOrWhiteSpace() ? "" : $"#{Uri.EscapeDataString(server.Remark)}")}"; + return + $"{scheme}://{server.UserID}@{server.Hostname}:{server.Port}?{string.Join("&", parameter.Select(p => $"{p.Key}={p.Value}"))}{(server.Remark.IsNullOrWhiteSpace() ? "" : $"#{Uri.EscapeDataString(server.Remark)}")}"; } } } \ No newline at end of file diff --git a/Netch/Servers/VLESS/VLESS.cs b/Netch/Servers/VLESS/VLESS.cs index e3908e7c..910fb842 100644 --- a/Netch/Servers/VLESS/VLESS.cs +++ b/Netch/Servers/VLESS/VLESS.cs @@ -38,7 +38,9 @@ namespace Netch.Servers.VLESS "tls", "xtls" }; + public static List FakeTypes => VMessGlobal.FakeTypes; + public static List TransferProtocols => VMessGlobal.TransferProtocols; public static List QUIC => VMessGlobal.QUIC; diff --git a/Netch/Servers/VLESS/VLESSForm/VLESSForm.cs b/Netch/Servers/VLESS/VLESSForm/VLESSForm.cs index e1fd6ba0..85213fa0 100644 --- a/Netch/Servers/VLESS/VLESSForm/VLESSForm.cs +++ b/Netch/Servers/VLESS/VLESSForm/VLESSForm.cs @@ -9,57 +9,34 @@ namespace Netch.Servers.VLESS.VLESSForm { server ??= new VLESS(); Server = server; - CreateTextBox("UUID", "UUID", - s => true, - s => server.UserID = s, - server.UserID); - CreateTextBox("EncryptMethod", "Encrypt Method", + CreateTextBox("UUID", "UUID", s => true, s => server.UserID = s, server.UserID); + CreateTextBox("EncryptMethod", + "Encrypt Method", s => true, s => server.EncryptMethod = !string.IsNullOrWhiteSpace(s) ? s : "none", server.EncryptMethod); - CreateTextBox("Flow", "Flow", - s => true, - s => server.Flow = s, - server.Flow); - CreateComboBox("TransferProtocol", "Transfer Protocol", + + CreateTextBox("Flow", "Flow", s => true, s => server.Flow = s, server.Flow); + CreateComboBox("TransferProtocol", + "Transfer Protocol", VLESSGlobal.TransferProtocols, s => server.TransferProtocol = s, server.TransferProtocol); - CreateComboBox("FakeType", "Fake Type", - VLESSGlobal.FakeTypes, - s => server.FakeType = s, - server.FakeType); - CreateTextBox("Host", "Host", - s => true, - s => server.Host = s, - server.Host); - CreateTextBox("Path", "Path", - s => true, - s => server.Path = s, - server.Path); - CreateComboBox("QUICSecurity", "QUIC Security", - VLESSGlobal.QUIC, - s => server.QUICSecure = s, - server.QUICSecure); - CreateTextBox("QUICSecret", "QUIC Secret", - s => true, - s => server.QUICSecret = s, - server.QUICSecret); - CreateComboBox("UseMux", "Use Mux", + + CreateComboBox("FakeType", "Fake Type", VLESSGlobal.FakeTypes, s => server.FakeType = s, server.FakeType); + CreateTextBox("Host", "Host", s => true, s => server.Host = s, server.Host); + CreateTextBox("Path", "Path", s => true, s => server.Path = s, server.Path); + CreateComboBox("QUICSecurity", "QUIC Security", VLESSGlobal.QUIC, s => server.QUICSecure = s, server.QUICSecure); + CreateTextBox("QUICSecret", "QUIC Secret", s => true, s => server.QUICSecret = s, server.QUICSecret); + CreateComboBox("UseMux", + "Use Mux", new List {"", "true", "false"}, - s => server.UseMux = s switch - { - "" => null, - "true" => true, - "false" => false, - _ => null - }, + s => server.UseMux = s switch {"" => null, "true" => true, "false" => false, _ => null}, server.UseMux?.ToString().ToLower() ?? ""); - CreateComboBox("TLSSecure", "TLS Secure", - VLESSGlobal.TLSSecure, - s => server.TLSSecureType = s, - server.TLSSecureType); + + CreateComboBox("TLSSecure", "TLS Secure", VLESSGlobal.TLSSecure, s => server.TLSSecureType = s, server.TLSSecureType); } + protected override string TypeName { get; } = "VLESS"; } } \ No newline at end of file diff --git a/Netch/Servers/VLESS/VLESSUtil.cs b/Netch/Servers/VLESS/VLESSUtil.cs index 6e317473..c63f143a 100644 --- a/Netch/Servers/VLESS/VLESSUtil.cs +++ b/Netch/Servers/VLESS/VLESSUtil.cs @@ -9,9 +9,13 @@ namespace Netch.Servers.VLESS public class VLESSUtil : IServerUtil { public ushort Priority { get; } = 2; + public string TypeName { get; } = "VLESS"; + public string FullName { get; } = "VLESS"; + public string ShortName { get; } = "VL"; + public string[] UriScheme { get; } = {"vless"}; public Server ParseJObject(in JObject j) diff --git a/Netch/Servers/VMess/Form/VMessForm.cs b/Netch/Servers/VMess/Form/VMessForm.cs index f065aed4..5a7eae0d 100644 --- a/Netch/Servers/VMess/Form/VMessForm.cs +++ b/Netch/Servers/VMess/Form/VMessForm.cs @@ -9,58 +9,29 @@ namespace Netch.Servers.VMess.Form { server ??= new VMess(); Server = server; - CreateTextBox("UserId", "User ID", - s => true, - s => server.UserID = s, - server.UserID); - CreateTextBox("AlterId", "Alter ID", - s => int.TryParse(s, out _), - s => server.AlterID = int.Parse(s), - server.AlterID.ToString(), - 76); - CreateComboBox("EncryptMethod", "Encrypt Method", - VMessGlobal.EncryptMethods, - s => server.EncryptMethod = s, - server.EncryptMethod); - CreateComboBox("TransferProtocol", "Transfer Protocol", + CreateTextBox("UserId", "User ID", s => true, s => server.UserID = s, server.UserID); + CreateTextBox("AlterId", "Alter ID", s => int.TryParse(s, out _), s => server.AlterID = int.Parse(s), server.AlterID.ToString(), 76); + CreateComboBox("EncryptMethod", "Encrypt Method", VMessGlobal.EncryptMethods, s => server.EncryptMethod = s, server.EncryptMethod); + CreateComboBox("TransferProtocol", + "Transfer Protocol", VMessGlobal.TransferProtocols, s => server.TransferProtocol = s, server.TransferProtocol); - CreateComboBox("FakeType", "Fake Type", - VMessGlobal.FakeTypes, - s => server.FakeType = s, - server.FakeType); - CreateTextBox("Host", "Host", - s => true, - s => server.Host = s, - server.Host); - CreateTextBox("Path", "Path", - s => true, - s => server.Path = s, - server.Path); - CreateComboBox("QUICSecurity", "QUIC Security", - VMessGlobal.QUIC, - s => server.QUICSecure = s, - server.QUICSecure); - CreateTextBox("QUICSecret", "QUIC Secret", - s => true, - s => server.QUICSecret = s, - server.QUICSecret); - CreateComboBox("UseMux", "Use Mux", + + CreateComboBox("FakeType", "Fake Type", VMessGlobal.FakeTypes, s => server.FakeType = s, server.FakeType); + CreateTextBox("Host", "Host", s => true, s => server.Host = s, server.Host); + CreateTextBox("Path", "Path", s => true, s => server.Path = s, server.Path); + CreateComboBox("QUICSecurity", "QUIC Security", VMessGlobal.QUIC, s => server.QUICSecure = s, server.QUICSecure); + CreateTextBox("QUICSecret", "QUIC Secret", s => true, s => server.QUICSecret = s, server.QUICSecret); + CreateComboBox("UseMux", + "Use Mux", new List {"", "true", "false"}, - s => server.UseMux = s switch - { - "" => null, - "true" => true, - "false" => false, - _ => null - }, + s => server.UseMux = s switch {"" => null, "true" => true, "false" => false, _ => null}, server.UseMux?.ToString().ToLower() ?? ""); - CreateComboBox("TLSSecure", "TLS Secure", - VMessGlobal.TLSSecure, - s => server.TLSSecureType = s, - server.TLSSecureType); + + CreateComboBox("TLSSecure", "TLS Secure", VMessGlobal.TLSSecure, s => server.TLSSecureType = s, server.TLSSecureType); } + protected override string TypeName { get; } = "VMess"; } } \ No newline at end of file diff --git a/Netch/Servers/VMess/VMess.cs b/Netch/Servers/VMess/VMess.cs index d3c2db83..ad28a44e 100644 --- a/Netch/Servers/VMess/VMess.cs +++ b/Netch/Servers/VMess/VMess.cs @@ -6,6 +6,7 @@ namespace Netch.Servers.VMess public class VMess : Server { private string _tlsSecureType = VMessGlobal.TLSSecure[0]; + public VMess() { Type = "VMess"; @@ -66,6 +67,7 @@ namespace Netch.Servers.VMess { if (value == "") value = "none"; + _tlsSecureType = value; } } diff --git a/Netch/Servers/VMess/VMessUtil.cs b/Netch/Servers/VMess/VMessUtil.cs index 78e693ac..83be6488 100644 --- a/Netch/Servers/VMess/VMessUtil.cs +++ b/Netch/Servers/VMess/VMessUtil.cs @@ -13,9 +13,13 @@ namespace Netch.Servers.VMess public class VMessUtil : IServerUtil { public ushort Priority { get; } = 3; + public string TypeName { get; } = "VMess"; + public string FullName { get; } = "VMess"; + public string ShortName { get; } = "V2"; + public string[] UriScheme { get; } = {"vmess"}; public Server ParseJObject(in JObject j) @@ -24,6 +28,7 @@ namespace Netch.Servers.VMess var server = j.ToObject(); if (server == null) return null; + string quic; if ((quic = j.GetValue("QUIC")?.ToString()) != null) server.QUICSecure = quic; @@ -61,6 +66,7 @@ namespace Netch.Servers.VMess path = server.Path, tls = server.TLSSecureType }); + return "vmess://" + ShareLink.URLSafeBase64Encode(vmessJson); } diff --git a/Netch/Updater/Updater.cs b/Netch/Updater/Updater.cs index b358655a..8babda7c 100644 --- a/Netch/Updater/Updater.cs +++ b/Netch/Updater/Updater.cs @@ -69,6 +69,7 @@ namespace Netch.Updater FileName = temp7za, Arguments = argument.ToString() }); + process?.WaitForExit(); return process?.ExitCode ?? 2; } @@ -119,6 +120,7 @@ namespace Netch.Updater foreach (var dir in Directory.GetDirectories(sourceDirName, "*", SearchOption.AllDirectories)) if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); + foreach (var f in Directory.GetFiles(sourceDirName, "*", SearchOption.AllDirectories)) try { diff --git a/Netch/Utils/Bandwidth.cs b/Netch/Utils/Bandwidth.cs index 40266988..7dcbd2e5 100644 --- a/Netch/Utils/Bandwidth.cs +++ b/Netch/Utils/Bandwidth.cs @@ -16,6 +16,7 @@ namespace Netch.Utils public static TraceEventSession tSession; private static readonly string[] Suffix = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}; + /// /// 计算流量 /// @@ -31,9 +32,11 @@ namespace Netch.Utils { if (level >= 6) // Suffix.Length - 1 break; + level++; size = (size ?? d) / step; } + return $@"{size ?? 0:0.##} {Suffix[level]}"; } @@ -60,6 +63,7 @@ namespace Netch.Utils case Guard instanceController: if (instanceController.Instance != null) instances.Add(instanceController.Instance); + break; } @@ -81,8 +85,7 @@ namespace Netch.Utils var processList = instances.Select(instance => instance.Id).ToList(); - Logging.Info("流量统计进程:" + string.Join(",", - instances.Select(instance => $"({instance.Id})" + instance.ProcessName).ToArray())); + Logging.Info("流量统计进程:" + string.Join(",", instances.Select(instance => $"({instance.Id})" + instance.ProcessName).ToArray())); received = 0; @@ -101,23 +104,21 @@ namespace Netch.Utils tSession.Source.Kernel.TcpIpRecv += data => { if (processList.Contains(data.ProcessID)) - { lock (counterLock) received += (ulong) data.size; - // Debug.WriteLine($"TcpIpRecv: {ToByteSize(data.size)}"); - } + // Debug.WriteLine($"TcpIpRecv: {ToByteSize(data.size)}"); }; + tSession.Source.Kernel.UdpIpRecv += data => { if (processList.Contains(data.ProcessID)) - { lock (counterLock) received += (ulong) data.size; - // Debug.WriteLine($"UdpIpRecv: {ToByteSize(data.size)}"); - } + // Debug.WriteLine($"UdpIpRecv: {ToByteSize(data.size)}"); }; + tSession.Source.Process(); }); @@ -125,9 +126,7 @@ namespace Netch.Utils { Task.Delay(1000).Wait(); lock (counterLock) - { Global.MainForm.OnBandwidthUpdated(received); - } } } diff --git a/Netch/Utils/Configuration.cs b/Netch/Utils/Configuration.cs index 8a7317db..0efebba8 100644 --- a/Netch/Utils/Configuration.cs +++ b/Netch/Utils/Configuration.cs @@ -40,12 +40,8 @@ namespace Netch.Utils } if (settingJObject?["Profiles"] != null && Global.Settings.Profiles.Any() && settingJObject["Profiles"].First()?["Index"] == null) - { foreach (var profile in Global.Settings.Profiles) - { profile.Index = Global.Settings.Profiles.IndexOf(profile); - } - } } catch (JsonException) { @@ -67,19 +63,15 @@ namespace Netch.Utils public static void Save() { if (!Directory.Exists(DATA_DIR)) - { Directory.CreateDirectory(DATA_DIR); - } File.WriteAllText(SETTINGS_JSON, - JsonConvert.SerializeObject( - Global.Settings, + JsonConvert.SerializeObject(Global.Settings, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore - } - )); + })); } } } \ No newline at end of file diff --git a/Netch/Utils/DNS.cs b/Netch/Utils/DNS.cs index d4849369..3f6d23cd 100644 --- a/Netch/Utils/DNS.cs +++ b/Netch/Utils/DNS.cs @@ -12,55 +12,12 @@ namespace Netch.Utils /// /// 缓存 /// - public static Hashtable Cache = new Hashtable(); + public static Hashtable Cache = new(); /// - /// 查询 - /// - /// 主机名 - /// - public static IPAddress Lookup(string hostname) - { - try - { - if (Cache.Contains(hostname)) - { - return Cache[hostname] as IPAddress; - } - - var task = Dns.GetHostAddressesAsync(hostname); - if (!task.Wait(1000)) - { - return null; - } - - if (task.Result.Length == 0) - { - return null; - } - - Cache.Add(hostname, task.Result[0]); - - return task.Result[0]; - } - catch (Exception) - { - return null; - } - } - - private static RegistryKey AdapterRegistry(bool write = false) - { - if (Global.Outbound.Adapter == null) - Utils.SearchOutboundAdapter(); - return Registry.LocalMachine.OpenSubKey( - $@"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{Global.Outbound.Adapter.Id}", write); - } - - /// - /// 出口网卡 DNS - /// - /// 依赖 + /// 出口网卡 DNS + /// + /// 依赖 /// public static string OutboundDNS { @@ -78,6 +35,44 @@ namespace Netch.Utils set => AdapterRegistry(true).SetValue("NameServer", value, RegistryValueKind.String); } + /// + /// 查询 + /// + /// 主机名 + /// + public static IPAddress Lookup(string hostname) + { + try + { + if (Cache.Contains(hostname)) + return Cache[hostname] as IPAddress; + + var task = Dns.GetHostAddressesAsync(hostname); + if (!task.Wait(1000)) + return null; + + if (task.Result.Length == 0) + return null; + + Cache.Add(hostname, task.Result[0]); + + return task.Result[0]; + } + catch (Exception) + { + return null; + } + } + + private static RegistryKey AdapterRegistry(bool write = false) + { + if (Global.Outbound.Adapter == null) + Utils.SearchOutboundAdapter(); + + return Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{Global.Outbound.Adapter.Id}", + write); + } + public static IEnumerable Split(string dns) { return dns.Split(',').Where(ip => !string.IsNullOrWhiteSpace(ip)).Select(ip => ip.Trim()); @@ -87,9 +82,7 @@ namespace Netch.Utils { result = Split(value).ToArray(); - return maxCount == 0 || result.Count() <= maxCount - && - result.All(ip => IPAddress.TryParse(ip, out _)); + return maxCount == 0 || result.Count() <= maxCount && result.All(ip => IPAddress.TryParse(ip, out _)); } public static string Join(IEnumerable dns) diff --git a/Netch/Utils/Firewall.cs b/Netch/Utils/Firewall.cs index 48da5788..7a9cc9e0 100644 --- a/Netch/Utils/Firewall.cs +++ b/Netch/Utils/Firewall.cs @@ -39,6 +39,7 @@ namespace Netch.Utils { if (rule.ApplicationName.StartsWith(Global.NetchDir)) return; + RemoveNetchFwRules(); } @@ -78,13 +79,11 @@ namespace Netch.Utils private static void AddFwRule(string ruleName, string exeFullPath) { - var rule = new FirewallWASRule( - ruleName, + var rule = new FirewallWASRule(ruleName, exeFullPath, FirewallAction.Allow, FirewallDirection.Inbound, - FirewallProfiles.Private | FirewallProfiles.Public | FirewallProfiles.Domain - ); + FirewallProfiles.Private | FirewallProfiles.Public | FirewallProfiles.Domain); FirewallManager.Instance.Rules.Add(rule); } diff --git a/Netch/Utils/HttpProxyHandler/HttpWebServer.cs b/Netch/Utils/HttpProxyHandler/HttpWebServer.cs index 23749580..08074a32 100644 --- a/Netch/Utils/HttpProxyHandler/HttpWebServer.cs +++ b/Netch/Utils/HttpProxyHandler/HttpWebServer.cs @@ -8,7 +8,7 @@ namespace Netch.Utils.HttpProxyHandler public class HttpWebServer { private HttpListener _listener; - private Func _responderMethod; + private readonly Func _responderMethod; public HttpWebServer(string[] prefixes, Func method) { @@ -17,8 +17,7 @@ namespace Netch.Utils.HttpProxyHandler _listener = new HttpListener(); if (!HttpListener.IsSupported) - throw new NotSupportedException( - "Needs Windows XP SP2, Server 2003 or later."); + throw new NotSupportedException("Needs Windows XP SP2, Server 2003 or later."); // URI prefixes are required, for example // "http://localhost:8080/index/". @@ -29,7 +28,7 @@ namespace Netch.Utils.HttpProxyHandler if (method == null) throw new ArgumentException("method"); - foreach (string s in prefixes) + foreach (var s in prefixes) _listener.Prefixes.Add(s); _responderMethod = method; @@ -41,42 +40,40 @@ namespace Netch.Utils.HttpProxyHandler } } - public HttpWebServer(Func method, params string[] prefixes) - : this(prefixes, method) + public HttpWebServer(Func method, params string[] prefixes) : this(prefixes, method) { } public void Run() { - ThreadPool.QueueUserWorkItem((o) => + ThreadPool.QueueUserWorkItem(o => { Logging.Info("Webserver running..."); try { while (_listener.IsListening) - { - ThreadPool.QueueUserWorkItem((c) => - { - var ctx = c as HttpListenerContext; - try + ThreadPool.QueueUserWorkItem(c => { - string rstr = _responderMethod(ctx.Request); - byte[] buf = Encoding.UTF8.GetBytes(rstr); - ctx.Response.StatusCode = 200; - ctx.Response.ContentType = "application/x-ns-proxy-autoconfig"; - ctx.Response.ContentLength64 = buf.Length; - ctx.Response.OutputStream.Write(buf, 0, buf.Length); - } - catch - { - } // suppress any exceptions - finally - { - // always close the stream - ctx.Response.OutputStream.Close(); - } - }, _listener.GetContext()); - } + var ctx = c as HttpListenerContext; + try + { + var rstr = _responderMethod(ctx.Request); + var buf = Encoding.UTF8.GetBytes(rstr); + ctx.Response.StatusCode = 200; + ctx.Response.ContentType = "application/x-ns-proxy-autoconfig"; + ctx.Response.ContentLength64 = buf.Length; + ctx.Response.OutputStream.Write(buf, 0, buf.Length); + } + catch + { + } // suppress any exceptions + finally + { + // always close the stream + ctx.Response.OutputStream.Close(); + } + }, + _listener.GetContext()); } catch (Exception ex) { diff --git a/Netch/Utils/HttpProxyHandler/PACServerHandle.cs b/Netch/Utils/HttpProxyHandler/PACServerHandle.cs index fcad45a0..9b951836 100644 --- a/Netch/Utils/HttpProxyHandler/PACServerHandle.cs +++ b/Netch/Utils/HttpProxyHandler/PACServerHandle.cs @@ -9,37 +9,35 @@ using WindowsProxy; namespace Netch.Utils.HttpProxyHandler { /// - /// 提供PAC功能支持 + /// 提供PAC功能支持 /// - class PACServerHandle + internal class PACServerHandle { - private static Hashtable httpWebServer = new Hashtable(); - private static Hashtable pacList = new Hashtable(); + private static readonly Hashtable httpWebServer = new(); + private static readonly Hashtable pacList = new(); public static void InitPACServer(string address) { try { if (!pacList.ContainsKey(address)) - { pacList.Add(address, GetPacList(address)); - } - string prefixes = string.Format("http://{0}:{1}/pac/", address, Global.Settings.Pac_Port); + var prefixes = string.Format("http://{0}:{1}/pac/", address, Global.Settings.Pac_Port); - HttpWebServer ws = new HttpWebServer(SendResponse, prefixes); + var ws = new HttpWebServer(SendResponse, prefixes); ws.Run(); if (!httpWebServer.ContainsKey(address) && ws != null) - { httpWebServer.Add(address, ws); - } + Global.Settings.Pac_Url = GetPacUrl(); using var service = new ProxyService { AutoConfigUrl = Global.Settings.Pac_Url }; + service.Pac(); Logging.Info(service.Set(service.Query()) + ""); @@ -55,12 +53,11 @@ namespace Netch.Utils.HttpProxyHandler { try { - string[] arrAddress = request.UserHostAddress.Split(':'); - string address = "127.0.0.1"; + var arrAddress = request.UserHostAddress.Split(':'); + var address = "127.0.0.1"; if (arrAddress.Length > 0) - { address = arrAddress[0]; - } + return pacList[address].ToString(); } catch (Exception ex) @@ -75,14 +72,14 @@ namespace Netch.Utils.HttpProxyHandler try { if (httpWebServer == null) - { return; - } + foreach (var key in httpWebServer.Keys) { - Logging.Info("Webserver Stop " + key.ToString()); - ((HttpWebServer)httpWebServer[key]).Stop(); + Logging.Info("Webserver Stop " + key); + ((HttpWebServer) httpWebServer[key]).Stop(); } + httpWebServer.Clear(); } catch (Exception ex) @@ -95,30 +92,31 @@ namespace Netch.Utils.HttpProxyHandler { try { - List lstProxy = new List(); + var lstProxy = new List(); lstProxy.Add(string.Format("PROXY {0}:{1};", address, Global.Settings.HTTPLocalPort)); var proxy = string.Join("", lstProxy.ToArray()); - string strPacfile = Path.Combine(Global.NetchDir, $"bin\\pac.txt"); + var strPacfile = Path.Combine(Global.NetchDir, "bin\\pac.txt"); var pac = File.ReadAllText(strPacfile, Encoding.UTF8).Replace("__PROXY__", proxy); return pac; } catch - { } + { + } + return "No pac content"; } /// - /// 获取PAC地址 + /// 获取PAC地址 /// /// public static string GetPacUrl() { - string pacUrl = string.Format("http://127.0.0.1:{0}/pac/?t={1}", Global.Settings.Pac_Port, - DateTime.Now.ToString("yyyyMMddHHmmssfff")); + var pacUrl = string.Format("http://127.0.0.1:{0}/pac/?t={1}", Global.Settings.Pac_Port, DateTime.Now.ToString("yyyyMMddHHmmssfff")); return pacUrl; } } -} +} \ No newline at end of file diff --git a/Netch/Utils/Logging.cs b/Netch/Utils/Logging.cs index a527a945..fa17a1fa 100644 --- a/Netch/Utils/Logging.cs +++ b/Netch/Utils/Logging.cs @@ -8,6 +8,8 @@ namespace Netch.Utils { public const string LogFile = "logging\\application.log"; + private static readonly object FileLock = new(); + /// /// 信息 /// @@ -35,14 +37,10 @@ namespace Netch.Utils Write(text, LogLevel.ERROR); } - private static readonly object FileLock = new object(); - private static void Write(string text, LogLevel logLevel) { lock (FileLock) - { File.AppendAllText(LogFile, $@"[{DateTime.Now}][{logLevel.ToString()}] {text}{Global.EOF}"); - } } } } \ No newline at end of file diff --git a/Netch/Utils/ModeHelper.cs b/Netch/Utils/ModeHelper.cs index e820498a..a25ed97b 100644 --- a/Netch/Utils/ModeHelper.cs +++ b/Netch/Utils/ModeHelper.cs @@ -19,10 +19,12 @@ namespace Netch.Utils { return fullName.Substring(ModeDirectory.Length); } + public static string GetFullPath(string relativeName) { return Path.Combine(ModeDirectory, relativeName); } + public static string GetFullPath(Mode mode) { return Path.Combine(ModeDirectory, mode.RelativePath); @@ -35,7 +37,8 @@ namespace Netch.Utils { Global.Modes.Clear(); - if (!Directory.Exists(MODE_DIR)) return; + if (!Directory.Exists(MODE_DIR)) + return; var stack = new Stack(); stack.Push(MODE_DIR); @@ -64,7 +67,8 @@ namespace Netch.Utils var mode = new Mode(fullName); var content = File.ReadAllLines(fullName); - if (content.Length == 0) return; + if (content.Length == 0) + return; for (var i = 0; i < content.Length; i++) { @@ -74,6 +78,7 @@ namespace Netch.Utils { if (text.First() != '#') return; + try { var splited = text.Substring(1).Split(',').Select(s => s.Trim()).ToArray(); @@ -125,15 +130,15 @@ namespace Netch.Utils public static bool SkipServerController(Server server, Mode mode) { return mode.Type switch - { - 0 => server switch - { - Socks5 => true, - Shadowsocks shadowsocks when !shadowsocks.HasPlugin() && Global.Settings.RedirectorSS => true, - _ => false - }, - _ => false - }; + { + 0 => server switch + { + Socks5 => true, + Shadowsocks shadowsocks when !shadowsocks.HasPlugin() && Global.Settings.RedirectorSS => true, + _ => false + }, + _ => false + }; } public static IModeController GetModeControllerByType(int type, out ushort? port, out string portName, out PortType portType) diff --git a/Netch/Utils/OnlyInstance.cs b/Netch/Utils/OnlyInstance.cs index 74f1164a..1cd1a017 100644 --- a/Netch/Utils/OnlyInstance.cs +++ b/Netch/Utils/OnlyInstance.cs @@ -32,6 +32,7 @@ namespace Netch.Utils PortHelper.CheckPort(Global.Settings.UDPSocketPort, PortType.UDP); if (i != tryLimit) Configuration.Save(); + break; } catch diff --git a/Netch/Utils/PortHelper.cs b/Netch/Utils/PortHelper.cs index 75937c84..5e03d302 100644 --- a/Netch/Utils/PortHelper.cs +++ b/Netch/Utils/PortHelper.cs @@ -40,10 +40,13 @@ namespace Netch.Utils CreateNoWindow = true } }; + process.OutputDataReceived += (s, e) => { - if (e.Data != null) lines.Add(e.Data); + if (e.Data != null) + lines.Add(e.Data); }; + process.Start(); process.BeginOutputReadLine(); process.WaitForExit(); @@ -86,6 +89,7 @@ namespace Netch.Utils break; } } + private static void CheckPortInUse(ushort port, PortType type) { switch (type) @@ -97,15 +101,18 @@ namespace Netch.Utils case PortType.TCP: if (NetInfo.GetActiveTcpListeners().Any(ipEndPoint => ipEndPoint.Port == port)) throw new PortInUseException(); + break; case PortType.UDP: if (NetInfo.GetActiveUdpListeners().Any(ipEndPoint => ipEndPoint.Port == port)) throw new PortInUseException(); + break; default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } } + /// /// 检查端口是否是保留端口 /// @@ -120,10 +127,12 @@ namespace Netch.Utils case PortType.TCP: if (TCPReservedRanges.Any(range => range.InRange(port))) throw new PortReservedException(); + break; case PortType.UDP: if (UDPReservedRanges.Any(range => range.InRange(port))) throw new PortReservedException(); + break; default: throw new ArgumentOutOfRangeException(nameof(type), type, null); @@ -166,15 +175,18 @@ namespace Netch.Utils public PortInUseException(string message) : base(message) { } + public PortInUseException() { } } + public class PortReservedException : Exception { public PortReservedException(string message) : base(message) { } + public PortReservedException() { } diff --git a/Netch/Utils/ServerHelper.cs b/Netch/Utils/ServerHelper.cs index a7c5341a..87d5bd11 100644 --- a/Netch/Utils/ServerHelper.cs +++ b/Netch/Utils/ServerHelper.cs @@ -13,7 +13,10 @@ namespace Netch.Utils { static ServerHelper() { - var serversUtilsTypes = Assembly.GetExecutingAssembly().GetExportedTypes().Where(type => type.GetInterfaces().Contains(typeof(IServerUtil))); + var serversUtilsTypes = Assembly.GetExecutingAssembly() + .GetExportedTypes() + .Where(type => type.GetInterfaces().Contains(typeof(IServerUtil))); + ServerUtils = serversUtilsTypes.Select(t => (IServerUtil) Activator.CreateInstance(t)).OrderBy(util => util.Priority); } @@ -25,6 +28,7 @@ namespace Netch.Utils private static bool _mux; public static readonly Range Range = new(0, int.MaxValue / 1000); + static DelayTestHelper() { Timer = new Timer @@ -43,6 +47,7 @@ namespace Netch.Utils { if (!ValueIsEnabled(Global.Settings.DetectionTick)) return; + Timer.Enabled = value; } } @@ -53,17 +58,18 @@ namespace Netch.Utils { return value != 0 && Range.InRange(value); } + public static event EventHandler TestDelayFinished; public static void TestAllDelay() { if (_mux) return; + try { _mux = true; - Parallel.ForEach(Global.Settings.Server, new ParallelOptions {MaxDegreeOfParallelism = 16}, - server => { server.Test(); }); + Parallel.ForEach(Global.Settings.Server, new ParallelOptions {MaxDegreeOfParallelism = 16}, server => { server.Test(); }); _mux = false; TestDelayFinished?.Invoke(null, new EventArgs()); } @@ -108,6 +114,7 @@ namespace Netch.Utils { if (string.IsNullOrEmpty(typeName)) return null; + return ServerUtils.FirstOrDefault(i => (i.TypeName ?? "").Equals(typeName)); } @@ -115,6 +122,7 @@ namespace Netch.Utils { if (string.IsNullOrEmpty(fullName)) return null; + return ServerUtils.FirstOrDefault(i => (i.FullName ?? "").Equals(fullName)); } diff --git a/Netch/Utils/ShareLink.cs b/Netch/Utils/ShareLink.cs index 8417974d..b3cf4f17 100644 --- a/Netch/Utils/ShareLink.cs +++ b/Netch/Utils/ShareLink.cs @@ -33,16 +33,17 @@ namespace Netch.Utils try { - list.AddRange(JsonConvert.DeserializeObject>(text).Select(server => new Shadowsocks - { - Hostname = server.server, - Port = server.server_port, - EncryptMethod = server.method, - Password = server.password, - Remark = server.remarks, - Plugin = server.plugin, - PluginOption = server.plugin_opts - })); + list.AddRange(JsonConvert.DeserializeObject>(text) + .Select(server => new Shadowsocks + { + 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) { @@ -98,6 +99,7 @@ namespace Netch.Utils var endIndex = text.IndexOf("://", StringComparison.Ordinal); if (endIndex == -1) throw new UriFormatException("Text is not a URI"); + return text.Substring(0, endIndex); } @@ -131,7 +133,9 @@ namespace Netch.Utils public static string GetNetchLink(Server s) { var server = (Server) s.Clone(); - return "Netch://" + URLSafeBase64Encode(JsonConvert.SerializeObject(server, new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore})); + return "Netch://" + + URLSafeBase64Encode(JsonConvert.SerializeObject(server, + new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore})); } #region Utils @@ -143,7 +147,8 @@ namespace Netch.Utils /// 解码后的字符串 public static string URLSafeBase64Decode(string text) { - return Encoding.UTF8.GetString(Convert.FromBase64String(text.Replace("-", "+").Replace("_", "/").PadRight(text.Length + (4 - text.Length % 4) % 4, '='))); + return Encoding.UTF8.GetString( + Convert.FromBase64String(text.Replace("-", "+").Replace("_", "/").PadRight(text.Length + (4 - text.Length % 4) % 4, '='))); } /// @@ -163,6 +168,7 @@ namespace Netch.Utils 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(); } diff --git a/Netch/Utils/StringExtension.cs b/Netch/Utils/StringExtension.cs index beaac8fb..e044b6c4 100644 --- a/Netch/Utils/StringExtension.cs +++ b/Netch/Utils/StringExtension.cs @@ -19,7 +19,9 @@ namespace Netch.Utils public static bool BeginWithAny(this string s, IEnumerable chars) { - if (s.IsNullOrEmpty()) return false; + if (s.IsNullOrEmpty()) + return false; + return chars.Contains(s[0]); } @@ -27,10 +29,12 @@ namespace Netch.Utils { foreach (var c in value) { - if (char.IsWhiteSpace(c)) continue; + if (char.IsWhiteSpace(c)) + continue; return false; } + return true; } @@ -39,7 +43,9 @@ namespace Netch.Utils string line; while ((line = reader.ReadLine()) != null) { - if (line.IsWhiteSpace()) continue; + if (line.IsWhiteSpace()) + continue; + yield return line; } } @@ -51,8 +57,10 @@ namespace Netch.Utils { if (new[] {'\\', '(', ')', '[', ']', '.'}.Any(s => s == t)) sb.Append(@"\"); + sb.Append(t); } + return sb.ToString(); } } diff --git a/Netch/Utils/Subscription.cs b/Netch/Utils/Subscription.cs index c886a40d..75e94d7d 100644 --- a/Netch/Utils/Subscription.cs +++ b/Netch/Utils/Subscription.cs @@ -9,15 +9,11 @@ namespace Netch.Utils { public static class Subscription { - private static readonly object ServerLock = new object(); + private static readonly object ServerLock = new(); public static async Task UpdateServersAsync(string proxyServer = default) { - await Task.WhenAll( - Global.Settings.SubscribeLink.Select(item => - Task.Run(() => UpdateServer(item, proxyServer)) - ).ToArray() - ); + await Task.WhenAll(Global.Settings.SubscribeLink.Select(item => Task.Run(() => UpdateServer(item, proxyServer))).ToArray()); } public static void UpdateServer(SubscribeLink item, string proxyServer) @@ -25,13 +21,15 @@ namespace Netch.Utils try { if (!item.Enable) - { return; - } + var request = WebUtil.CreateRequest(item.Link); - if (!string.IsNullOrEmpty(item.UserAgent)) request.UserAgent = item.UserAgent; - if (!string.IsNullOrEmpty(proxyServer)) request.Proxy = new WebProxy(proxyServer); + if (!string.IsNullOrEmpty(item.UserAgent)) + request.UserAgent = item.UserAgent; + + if (!string.IsNullOrEmpty(proxyServer)) + request.Proxy = new WebProxy(proxyServer); List servers; @@ -41,7 +39,8 @@ namespace Netch.Utils else throw new Exception($"{item.Remark} Response Status Code: {rep.StatusCode}"); - foreach (var server in servers) server.Group = item.Remark; + foreach (var server in servers) + server.Group = item.Remark; lock (ServerLock) { diff --git a/Netch/Utils/TUNTAP.cs b/Netch/Utils/TUNTAP.cs index 218dd60e..b966c448 100644 --- a/Netch/Utils/TUNTAP.cs +++ b/Netch/Utils/TUNTAP.cs @@ -23,18 +23,14 @@ namespace Netch.Utils var adaptersRegistry = Registry.LocalMachine.OpenSubKey(ADAPTER_KEY); foreach (var adapterRegistryName in adaptersRegistry.GetSubKeyNames()) - { if (adapterRegistryName != "Configuration" && adapterRegistryName != "Properties") { var adapterRegistry = adaptersRegistry.OpenSubKey(adapterRegistryName); var adapterComponentId = adapterRegistry.GetValue("ComponentId", "").ToString(); if (adapterComponentId == TUNTAP_COMPONENT_ID_0901 || adapterComponentId == TUNTAP_COMPONENT_ID_0801) - { return adapterRegistry.GetValue("NetCfgInstanceId", "").ToString(); - } } - } } catch (Exception e) { @@ -66,25 +62,29 @@ namespace Netch.Utils } /// - /// 卸载tap网卡 + /// 卸载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")}}; + var installProcess = new Process + {StartInfo = {WindowStyle = ProcessWindowStyle.Hidden, FileName = Path.Combine("bin/tap-driver", "deltapall.bat")}}; + installProcess.Start(); installProcess.WaitForExit(); installProcess.Close(); } /// - /// 安装tap网卡 + /// 安装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")}}; + var installProcess = new Process + {StartInfo = {WindowStyle = ProcessWindowStyle.Hidden, FileName = Path.Combine("bin/tap-driver", "addtap.bat")}}; + installProcess.Start(); installProcess.WaitForExit(); installProcess.Close(); diff --git a/Netch/Utils/Utils.cs b/Netch/Utils/Utils.cs index b267348b..ca70e5fe 100644 --- a/Netch/Utils/Utils.cs +++ b/Netch/Utils/Utils.cs @@ -30,6 +30,7 @@ namespace Netch.Utils Arguments = path, UseShellExecute = true }); + return true; } catch @@ -140,8 +141,7 @@ namespace Netch.Utils public static void SearchOutboundAdapter(bool logging = true) { // 寻找出口适配器 - if (IpHlpApi.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse("114.114.114.114").GetAddressBytes(), 0), - 0, out var pRoute) != 0) + if (IpHlpApi.GetBestRoute(BitConverter.ToUInt32(IPAddress.Parse("114.114.114.114").GetAddressBytes(), 0), 0, out var pRoute) != 0) { Logging.Error("GetBestRoute 搜索失败"); throw new Exception("GetBestRoute 搜索失败"); @@ -149,17 +149,19 @@ namespace Netch.Utils Global.Outbound.Index = (int) pRoute.dwForwardIfIndex; // 根据 IP Index 寻找 出口适配器 - var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ => - { - try + var adapter = NetworkInterface.GetAllNetworkInterfaces() + .First(_ => { - return _.GetIPProperties().GetIPv4Properties().Index == Global.Outbound.Index; - } - catch - { - return false; - } - }); + try + { + return _.GetIPProperties().GetIPv4Properties().Index == Global.Outbound.Index; + } + catch + { + return false; + } + }); + Global.Outbound.Adapter = adapter; Global.Outbound.Gateway = new IPAddress(pRoute.dwForwardNextHop.S_un_b); @@ -186,7 +188,10 @@ namespace Netch.Utils if (e.Index < 0) return; - TextRenderer.DrawText(e.Graphics, cbx.Items[e.Index].ToString(), cbx.Font, e.Bounds, + TextRenderer.DrawText(e.Graphics, + cbx.Items[e.Index].ToString(), + cbx.Font, + e.Bounds, (e.State & DrawItemState.Selected) == DrawItemState.Selected ? SystemColors.HighlightText : cbx.ForeColor, TextFormatFlags.HorizontalCenter); } @@ -271,7 +276,9 @@ namespace Netch.Utils { case TextBox _: case ComboBox _: - if (((Control) component).ForeColor != color) ((Control) component).ForeColor = color; + if (((Control) component).ForeColor != color) + ((Control) component).ForeColor = color; + break; } } diff --git a/Netch/Utils/WebUtil.cs b/Netch/Utils/WebUtil.cs index 65d59bee..2ead5298 100644 --- a/Netch/Utils/WebUtil.cs +++ b/Netch/Utils/WebUtil.cs @@ -8,14 +8,14 @@ namespace Netch.Utils { public static class WebUtil { + public const string DefaultUserAgent = + @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.55"; + static WebUtil() { ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; } - public const string DefaultUserAgent = - @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.55"; - private static int DefaultGetTimeout => Global.Settings.RequestTimeout; public static HttpWebRequest CreateRequest(string url, int? timeout = null, string userAgent = null) @@ -99,4 +99,4 @@ namespace Netch.Utils fileStream.Flush(); } } -} +} \ No newline at end of file diff --git a/Netch/Utils/i18N.cs b/Netch/Utils/i18N.cs index 9b0dd056..4eaeac10 100644 --- a/Netch/Utils/i18N.cs +++ b/Netch/Utils/i18N.cs @@ -64,7 +64,8 @@ namespace Netch.Utils var data = JsonConvert.DeserializeObject>(text); - if (data == null) return; + if (data == null) + return; Data = new Hashtable(); foreach (var v in data) @@ -84,6 +85,7 @@ namespace Netch.Utils a.Append(Data.Contains(t) ? Data[t].ToString() : t); else a.Append(t); + return a.ToString(); } @@ -100,31 +102,34 @@ namespace Netch.Utils { var translateFile = new List {"System", "zh-CN", "en-US"}; - if (!Directory.Exists("i18n")) return translateFile; + if (!Directory.Exists("i18n")) + return translateFile; + translateFile.AddRange(Directory.GetFiles("i18n", "*").Select(fileName => fileName.Substring(5))); return translateFile; } public static void TranslateForm(in Control c) { - Utils.ComponentIterator(c, component => - { - switch (component) + Utils.ComponentIterator(c, + component => { - case TextBoxBase _: - case ListControl _: - break; - case Control c: - c.Text = Translate(c.Text); - break; - case ToolStripItem c: - c.Text = Translate(c.Text); - break; - case ColumnHeader c: - c.Text = Translate(c.Text); - break; - } - }); + switch (component) + { + case TextBoxBase _: + case ListControl _: + break; + case Control c: + c.Text = Translate(c.Text); + break; + case ToolStripItem c: + c.Text = Translate(c.Text); + break; + case ColumnHeader c: + c.Text = Translate(c.Text); + break; + } + }); } } } \ No newline at end of file diff --git a/Netch/Win32Native.cs b/Netch/Win32Native.cs index af732b2b..cb7da4e4 100644 --- a/Netch/Win32Native.cs +++ b/Netch/Win32Native.cs @@ -8,4 +8,4 @@ namespace Netch [DllImport("User32", CharSet = CharSet.Auto, ExactSpelling = true)] public static extern IntPtr GetForegroundWindow(); } -} +} \ No newline at end of file