This commit is contained in:
ChsBuffer
2021-02-20 18:15:16 +08:00
parent 021f940957
commit ac0800ec56
79 changed files with 924 additions and 890 deletions

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -175,6 +175,7 @@ namespace Netch.Controllers
Task.Run(() => ServerController?.Stop()),
Task.Run(() => ModeController?.Stop())
};
await Task.WhenAll(tasks);
ModeController = null;
ServerController = null;

View File

@@ -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);

View File

@@ -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();
}
/// <summary>
/// 启动 NatTypeTester
/// </summary>
@@ -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();
}
}
}

View File

@@ -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<string> ipNetworks, RouteType routeType,
int metric = 0)
private void RouteAction(Action action, in IEnumerable<string> ipNetworks, RouteType routeType, int metric = 0)
{
foreach (var address in ipNetworks)
RouteAction(action, address, routeType, metric);

View File

@@ -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)

View File

@@ -32,4 +32,4 @@ namespace Netch.Forms
Process.Start("https://www.mansora.co");
}
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -14,32 +14,31 @@ namespace Netch.Forms
/// <param name="level">弹窗等级 (标题, 图标)</param>
/// <param name="confirm">需要确认</param>
/// <param name="owner">阻止 owner Focus() 直到 Messageox 被关闭</param>
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);
}
}
}

View File

@@ -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<string>());
mode.WriteFile();

View File

@@ -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<Control, Func<string, bool>> _checkActions = new();
private readonly Dictionary<Control, Action<object>> _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<string, bool> check, Action<string> save, string value, int width = InputBoxWidth)
protected void CreateTextBox(string name,
string remark,
Func<string, bool> check,
Action<string> 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<string> values, Action<string> 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<bool> 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<Control, Func<string, bool>> _checkActions = new Dictionary<Control, Func<string, bool>>();
private readonly Dictionary<Control, Action<object>> _saveActions = new Dictionary<Control, Action<object>>();
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;
}
}

View File

@@ -15,6 +15,7 @@ namespace Netch.Forms
private readonly Dictionary<Control, Func<string, bool>> _checkActions = new();
private readonly Dictionary<Control, Action<Control>> _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<ushort>(HTTPPortTextBox,
p => p.ToString() != Socks5PortTextBox.Text && p.ToString() != RedirectorTextBox.Text,
p => Global.Settings.HTTPLocalPort = p,
Global.Settings.HTTPLocalPort);
BindTextBox<ushort>(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<int>(ProfileCountTextBox,
i => i > -1,
i => Global.Settings.ProfileCount = i,
Global.Settings.ProfileCount);
BindTextBox<int>(ProfileCountTextBox, i => i > -1, i => Global.Settings.ProfileCount = i, Global.Settings.ProfileCount);
BindTextBox<int>(DetectionTickTextBox,
i => ServerHelper.DelayTestHelper.Range.InRange(i),
i => Global.Settings.DetectionTick = i,
Global.Settings.DetectionTick);
BindTextBox<int>(StartedPingIntervalTextBox,
_ => true,
i => Global.Settings.StartedPingInterval = i,
@@ -83,10 +72,7 @@ namespace Netch.Forms
InitSTUN();
BindTextBox<string>(AclAddrTextBox,
s => true,
s => Global.Settings.ACL = s,
Global.Settings.ACL);
BindTextBox<string>(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<int>(mtuTextBox,
i => true,
i => Global.Settings.V2RayConfig.KcpConfig.mtu = i,
Global.Settings.V2RayConfig.KcpConfig.mtu);
BindTextBox<int>(ttiTextBox,
i => true,
i => Global.Settings.V2RayConfig.KcpConfig.tti = i,
Global.Settings.V2RayConfig.KcpConfig.tti);
BindTextBox<int>(mtuTextBox, i => true, i => Global.Settings.V2RayConfig.KcpConfig.mtu = i, Global.Settings.V2RayConfig.KcpConfig.mtu);
BindTextBox<int>(ttiTextBox, i => true, i => Global.Settings.V2RayConfig.KcpConfig.tti = i, Global.Settings.V2RayConfig.KcpConfig.tti);
BindTextBox<int>(uplinkCapacityTextBox,
i => true,
i => Global.Settings.V2RayConfig.KcpConfig.uplinkCapacity = i,
Global.Settings.V2RayConfig.KcpConfig.uplinkCapacity);
BindTextBox<int>(downlinkCapacityTextBox,
i => true,
i => Global.Settings.V2RayConfig.KcpConfig.downlinkCapacity = i,
Global.Settings.V2RayConfig.KcpConfig.downlinkCapacity);
BindTextBox<int>(readBufferSizeTextBox,
i => true,
i => Global.Settings.V2RayConfig.KcpConfig.readBufferSize = i,
Global.Settings.V2RayConfig.KcpConfig.readBufferSize);
BindTextBox<int>(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<T>(TextBox control, Func<T, bool> check, Action<T> 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<bool> 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;
}
}
}

View File

@@ -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)

View File

@@ -20,6 +20,9 @@ namespace Netch
/// </summary>
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";
/// <summary>
/// 出口适配器
/// </summary>
public static class Outbound
{
/// <summary>
/// 索引
/// </summary>
public static int Index = -1;
/// <summary>
/// 地址
/// </summary>
public static IPAddress Address => Adapter.GetIPProperties().UnicastAddresses.First(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork).Address;
/// <summary>
/// 网关
/// </summary>
public static IPAddress Gateway;
public static NetworkInterface Adapter;
}
/// <summary>
/// TUN/TAP 适配器
/// </summary>
public static class TUNTAP
{
/// <summary>
/// 适配器
/// </summary>
public static NetworkInterface Adapter;
/// <summary>
/// 索引
/// </summary>
public static int Index = -1;
/// <summary>
/// 组件 ID
/// </summary>
public static string ComponentID = string.Empty;
}
/// <summary>
/// 用于读取和写入的配置
/// </summary>
public static Setting Settings = new Setting();
public static Setting Settings = new();
/// <summary>
/// 用于存储模式
/// </summary>
public static readonly List<Mode> Modes = new List<Mode>();
public static readonly List<Mode> Modes = new();
/// <summary>
/// Windows Job API
/// Windows Job API
/// </summary>
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();
}
/// <summary>
/// 出口适配器
/// </summary>
public static class Outbound
{
/// <summary>
/// 索引
/// </summary>
public static int Index = -1;
/// <summary>
/// 网关
/// </summary>
public static IPAddress Gateway;
public static NetworkInterface Adapter;
/// <summary>
/// 地址
/// </summary>
public static IPAddress Address => Adapter.GetIPProperties()
.UnicastAddresses.First(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork)
.Address;
}
/// <summary>
/// TUN/TAP 适配器
/// </summary>
public static class TUNTAP
{
/// <summary>
/// 适配器
/// </summary>
public static NetworkInterface Adapter;
/// <summary>
/// 索引
/// </summary>
public static int Index = -1;
/// <summary>
/// 组件 ID
/// </summary>
public static string ComponentID = string.Empty;
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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";
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -7,9 +7,13 @@ namespace Netch.Models.GitHubRelease
public struct SuffixVersion : ICloneable, IComparable, IComparable<SuffixVersion>, IEquatable<SuffixVersion>
{
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;
}
/// <summary>
///
/// </summary>
/// <param name="other"></param>
/// <returns>
/// greater than 0 newer
/// greater than 0 newer
/// </returns>
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

View File

@@ -9,4 +9,4 @@ namespace Netch.Models.GitHubRelease
return VersionUtil.CompareVersion(x?.ToString(), y?.ToString());
}
}
}
}

View File

@@ -8,9 +8,7 @@ namespace Netch.Models.GitHubRelease
public static Release GetLatestRelease(IEnumerable<Release> 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());

View File

@@ -41,10 +41,12 @@ namespace Netch.Models
/// <para />
/// </summary>
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

View File

@@ -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 @@
}
/// <summary>
/// Return a dummy one.
/// Return a dummy one.
/// </summary>
public Profile()
{
}
}
}
}

View File

@@ -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)
{

View File

@@ -30,7 +30,6 @@
/// </summary>
Stopped,
/// <summary>
/// 退出中
/// </summary>
@@ -43,6 +42,7 @@
{
if (state == State.Waiting)
return "Waiting for command";
return state.ToString();
}
}

View File

@@ -7,16 +7,16 @@
/// </summary>
public bool Enable = true;
/// <summary>
/// 备注
/// </summary>
public string Remark;
/// <summary>
/// 链接
/// </summary>
public string Link;
/// <summary>
/// 备注
/// </summary>
public string Remark;
/// <summary>
/// User Agent
/// </summary>

View File

@@ -5,7 +5,7 @@ namespace Netch
public static class NativeMethods
{
/// <summary>
/// 创建路由规则
/// 创建路由规则
/// </summary>
/// <param name="address">目标地址</param>
/// <param name="cidr">CIDR</param>
@@ -17,7 +17,7 @@ namespace Netch
public static extern bool CreateRoute(string address, int cidr, string gateway, int index, int metric = 0);
/// <summary>
/// 删除路由规则
/// 删除路由规则
/// </summary>
/// <param name="address">目标地址</param>
/// <param name="cidr">掩码地址</param>

View File

@@ -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();

View File

@@ -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";
}
}

View File

@@ -9,11 +9,6 @@ namespace Netch.Servers.Shadowsocks.Models.SSD
/// </summary>
public string airport;
/// <summary>
/// 端口
/// </summary>
public ushort port;
/// <summary>
/// 加密方式
/// </summary>
@@ -34,6 +29,11 @@ namespace Netch.Servers.Shadowsocks.Models.SSD
/// </summary>
public string plugin_options;
/// <summary>
/// 端口
/// </summary>
public ushort port;
/// <summary>
/// 服务器数组
/// </summary>

View File

@@ -2,16 +2,6 @@
{
public class SSDServer
{
/// <summary>
/// 服务器地址
/// </summary>
public string server;
/// <summary>
/// 端口
/// </summary>
public ushort port;
/// <summary>
/// 加密方式
/// </summary>
@@ -32,9 +22,18 @@
/// </summary>
public string plugin_options;
/// <summary>
/// 端口
/// </summary>
public ushort port;
/// <summary>
/// 备注
/// </summary>
public string remarks;
/// <summary>
/// 服务器地址
/// </summary>
public string server;
}
}

View File

@@ -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; }
}
}

View File

@@ -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<string> StartedKeywords { get; } = new[] {"listening at"};
protected override IEnumerable<string> 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)}");

View File

@@ -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<Server> 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<Server> ParseSsdUri(string s)
{
var json = JsonConvert.DeserializeObject<Main>(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://(?<base64>.+?)@(?<server>.+):(?<port>\d+)");
var parser = new Regex(@"^(?<method>.+?):(?<password>.+)$");
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(@"^((?<method>.+?):(?<password>.+)@(?<server>.+):(?<port>\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;
}
}
}

View File

@@ -5,6 +5,11 @@ namespace Netch.Servers.Shadowsocks
{
public class Shadowsocks : Server
{
public Shadowsocks()
{
Type = "SS";
}
/// <summary>
/// 加密方式
/// </summary>
@@ -25,12 +30,10 @@ namespace Netch.Servers.Shadowsocks
/// </summary>
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
/// <summary>
/// SS 加密列表
/// </summary>
public static readonly List<string> EncryptMethods = new List<string>
public static readonly List<string> EncryptMethods = new()
{
"rc4-md5",
"aes-128-gcm",

View File

@@ -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";
}
}

View File

@@ -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");

View File

@@ -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<ShadowsocksR>();
}
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
/// <summary>
/// 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
/// </summary>
/// <param name="text"></param>
@@ -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[]
{

View File

@@ -5,6 +5,11 @@ namespace Netch.Servers.ShadowsocksR
{
public class ShadowsocksR : Server
{
public ShadowsocksR()
{
Type = "SSR";
}
/// <summary>
/// 加密方式
/// </summary>
@@ -34,11 +39,6 @@ namespace Netch.Servers.ShadowsocksR
/// 协议参数
/// </summary>
public string ProtocolParam { get; set; }
public ShadowsocksR()
{
Type = "SSR";
}
}
public class SSRGlobal
@@ -46,7 +46,7 @@ namespace Netch.Servers.ShadowsocksR
/// <summary>
/// SSR 协议列表
/// </summary>
public static readonly List<string> Protocols = new List<string>
public static readonly List<string> Protocols = new()
{
"origin",
"verify_deflate",
@@ -59,7 +59,7 @@ namespace Netch.Servers.ShadowsocksR
/// <summary>
/// SSR 混淆列表
/// </summary>
public static readonly List<string> OBFSs = new List<string>
public static readonly List<string> OBFSs = new()
{
"plain",
"http_simple",
@@ -70,7 +70,7 @@ namespace Netch.Servers.ShadowsocksR
/// <summary>
/// SS/SSR 加密方式
/// </summary>
public static readonly List<string> EncryptMethods = new List<string>
public static readonly List<string> EncryptMethods = new()
{
"none",
"table",

View File

@@ -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";
}
}

View File

@@ -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<Server> 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};
}

View File

@@ -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);
}
}
}

View File

@@ -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";
}
}

View File

@@ -4,11 +4,6 @@ namespace Netch.Servers.Trojan.Models
{
public class TrojanConfig
{
/// <summary>
/// 启动类型
/// </summary>
public string run_type = "client";
/// <summary>
/// 监听地址
/// </summary>
@@ -19,6 +14,16 @@ namespace Netch.Servers.Trojan.Models
/// </summary>
public int local_port = 2801;
/// <summary>
/// 日志级别
/// </summary>
public int log_level = 1;
/// <summary>
/// 密码
/// </summary>
public List<string> password;
/// <summary>
/// 远端地址
/// </summary>
@@ -28,47 +33,41 @@ namespace Netch.Servers.Trojan.Models
/// 远端端口
/// </summary>
public int remote_port;
/// <summary>
/// 密码
/// 启动类型
/// </summary>
public List<string> password;
public string run_type = "client";
/// <summary>
/// 日志级别
/// </summary>
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<string> alpn = new List<string>
public List<string> 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;
}
}

View File

@@ -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<string> StartedKeywords { get; } = new[] {"started"};
protected override IEnumerable<string> 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");
}

View File

@@ -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://(?<psk>.+?)@(?<server>.+):(?<port>\d+)");
var match = finder.Match(text);
if (!match.Success)
{
throw new FormatException();
}
data.Password = match.Groups["psk"].Value;
data.Hostname = match.Groups["server"].Value;

View File

@@ -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";
}

View File

@@ -5,59 +5,58 @@
/// </summary>
public class V2rayNSharing
{
/// <summary>
/// 链接版本
/// </summary>
public string v = string.Empty;
/// <summary>
/// 备注
/// </summary>
public string ps = string.Empty;
/// <summary>
/// 地址
/// </summary>
public string add = string.Empty;
/// <summary>
/// 端口
/// </summary>
public string port = string.Empty;
/// <summary>
/// 用户 ID
/// </summary>
public string id = string.Empty;
/// <summary>
/// 额外 ID
/// </summary>
public string aid = string.Empty;
/// <summary>
/// 传输协议
/// </summary>
public string net = string.Empty;
/// <summary>
/// 伪装类型
/// </summary>
public string type = string.Empty;
/// <summary>
/// 伪装域名HTTPWS
/// </summary>
public string host = string.Empty;
/// <summary>
/// 用户 ID
/// </summary>
public string id = string.Empty;
/// <summary>
/// 传输协议
/// </summary>
public string net = string.Empty;
/// <summary>
/// 伪装路径
/// </summary>
public string path = string.Empty;
/// <summary>
/// 端口
/// </summary>
public string port = string.Empty;
/// <summary>
/// 备注
/// </summary>
public string ps = string.Empty;
/// <summary>
/// 是否使用 TLS
/// </summary>
public string tls = string.Empty;
/// <summary>
/// 伪装类型
/// </summary>
public string type = string.Empty;
/// <summary>
/// 链接版本
/// </summary>
public string v = string.Empty;
}
}

View File

@@ -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> {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> {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
};

View File

@@ -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));

View File

@@ -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}://(?<guid>.+?)@(?<server>.+):(?<port>\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)}")}";
}
}
}

View File

@@ -38,7 +38,9 @@ namespace Netch.Servers.VLESS
"tls",
"xtls"
};
public static List<string> FakeTypes => VMessGlobal.FakeTypes;
public static List<string> TransferProtocols => VMessGlobal.TransferProtocols;
public static List<string> QUIC => VMessGlobal.QUIC;

View File

@@ -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<string> {"", "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";
}
}

View File

@@ -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)

View File

@@ -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<string> {"", "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";
}
}

View File

@@ -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;
}
}

View File

@@ -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<VMess>();
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);
}

View File

@@ -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
{

View File

@@ -16,6 +16,7 @@ namespace Netch.Utils
public static TraceEventSession tSession;
private static readonly string[] Suffix = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
/// <summary>
/// 计算流量
/// </summary>
@@ -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);
}
}
}

View File

@@ -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
}
));
}));
}
}
}

View File

@@ -12,55 +12,12 @@ namespace Netch.Utils
/// <summary>
/// 缓存
/// </summary>
public static Hashtable Cache = new Hashtable();
public static Hashtable Cache = new();
/// <summary>
/// 查询
/// </summary>
/// <param name="hostname">主机名</param>
/// <returns></returns>
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);
}
/// <summary>
/// 出口网卡 DNS
/// <para></para>
/// 依赖 <see cref="Global.Outbound.Adapter"/>
/// 出口网卡 DNS
/// <para></para>
/// 依赖 <see cref="Global.Outbound.Adapter" />
/// </summary>
public static string OutboundDNS
{
@@ -78,6 +35,44 @@ namespace Netch.Utils
set => AdapterRegistry(true).SetValue("NameServer", value, RegistryValueKind.String);
}
/// <summary>
/// 查询
/// </summary>
/// <param name="hostname">主机名</param>
/// <returns></returns>
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<string> 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<string> dns)

View File

@@ -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);
}

View File

@@ -8,7 +8,7 @@ namespace Netch.Utils.HttpProxyHandler
public class HttpWebServer
{
private HttpListener _listener;
private Func<HttpListenerRequest, string> _responderMethod;
private readonly Func<HttpListenerRequest, string> _responderMethod;
public HttpWebServer(string[] prefixes, Func<HttpListenerRequest, string> 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<HttpListenerRequest, string> method, params string[] prefixes)
: this(prefixes, method)
public HttpWebServer(Func<HttpListenerRequest, string> 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)
{

View File

@@ -9,37 +9,35 @@ using WindowsProxy;
namespace Netch.Utils.HttpProxyHandler
{
/// <summary>
/// 提供PAC功能支持
/// 提供PAC功能支持
/// </summary>
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<string> lstProxy = new List<string>();
var lstProxy = new List<string>();
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";
}
/// <summary>
/// 获取PAC地址
/// 获取PAC地址
/// </summary>
/// <returns></returns>
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;
}
}
}
}

View File

@@ -8,6 +8,8 @@ namespace Netch.Utils
{
public const string LogFile = "logging\\application.log";
private static readonly object FileLock = new();
/// <summary>
/// 信息
/// </summary>
@@ -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}");
}
}
}
}

View File

@@ -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<string>();
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)

View File

@@ -32,6 +32,7 @@ namespace Netch.Utils
PortHelper.CheckPort(Global.Settings.UDPSocketPort, PortType.UDP);
if (i != tryLimit)
Configuration.Save();
break;
}
catch

View File

@@ -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);
}
}
/// <summary>
/// 检查端口是否是保留端口
/// </summary>
@@ -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()
{
}

View File

@@ -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));
}

View File

@@ -33,16 +33,17 @@ namespace Netch.Utils
try
{
list.AddRange(JsonConvert.DeserializeObject<List<ShadowsocksConfig>>(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<List<ShadowsocksConfig>>(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
/// <returns>解码后的字符串</returns>
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, '=')));
}
/// <summary>
@@ -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();
}

View File

@@ -19,7 +19,9 @@ namespace Netch.Utils
public static bool BeginWithAny(this string s, IEnumerable<char> 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();
}
}

View File

@@ -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<Server> 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)
{

View File

@@ -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
}
/// <summary>
/// 卸载tap网卡
/// 卸载tap网卡
/// </summary>
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();
}
/// <summary>
/// 安装tap网卡
/// 安装tap网卡
/// </summary>
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();

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -64,7 +64,8 @@ namespace Netch.Utils
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(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<string> {"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;
}
});
}
}
}

View File

@@ -8,4 +8,4 @@ namespace Netch
[DllImport("User32", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetForegroundWindow();
}
}
}