Compare commits

..

22 Commits
1.7.1 ... 1.7.2

Author SHA1 Message Date
ChsBuffer
d15206b803 Bump version to 1.7.2 2021-01-15 19:57:28 +08:00
ChsBuffer
754753300d Refactor Test All Server Delay Tick 2021-01-15 19:53:44 +08:00
ChsBuffer
aa021aaf79 Fix Build 2021-01-15 19:43:09 +08:00
ChsBuffer
c50ca563b2 Improve Force Exit 2021-01-15 19:42:49 +08:00
ChsBuffer
a1fdf7ead2 Update SettingForm 2021-01-15 18:09:36 +08:00
ChsBuffer
c9da7197ba Refactor: Merge MainForm 2021-01-15 16:24:59 +08:00
ChsBuffer
5ff26e97b4 trim 2021-01-14 20:13:15 +08:00
ChsBuffer
06049cb06f Refactor Servers Test Delay Interval 2021-01-14 19:52:23 +08:00
ChsBuffer
8cf765de92 Feat: Update Download Progress 2021-01-14 16:28:14 +08:00
ChsBuffer
2b53a7ca9e Refactor UpdateChecker.cs 2021-01-14 15:54:17 +08:00
ChsBuffer
c7c0a2a698 Update README 2021-01-13 21:16:53 +08:00
ChsBuffer
1d072214ee Refactor Check Port 2021-01-13 20:22:15 +08:00
AmazingDM
135ff642bd Update NF driver to 1.6.0.2 2021-01-13 09:53:52 +08:00
Connection Refused
4c274a1888 Update zh-CN 2021-01-13 02:23:11 +08:00
ChsBuffer
59cfc071cc [Break] Rename UpdateSubscribeatWhenOpened to UpdateServersWhenOpened 2021-01-12 19:13:14 +08:00
ChsBuffer
8cf32e5d37 Feat: MainForm.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem,
"Update server at startup" uses proxy based on the last update servers operation
2021-01-12 19:02:27 +08:00
ChsBuffer
7a1e5b58b9 SubscribeForm: Refactor 2021-01-12 18:28:38 +08:00
ChsBuffer
651fbb6ff9 SubscribeForm: Fix Enable Checked Event, Organize code 2021-01-12 17:46:52 +08:00
ChsBuffer
45bf31e9f3 Fix SubscribeForm Edit 2021-01-12 17:02:57 +08:00
AmazingDM
f7c9ab4028 Update NTT to 3.4 2021-01-11 17:10:19 +08:00
AmazingDM
717dc55055 Delete subscribeForm alert 2021-01-11 11:54:25 +08:00
ChsBuffer
a0c4dd3192 Replace NetchCore with RouteHelper 2021-01-10 20:58:49 +08:00
30 changed files with 1986 additions and 2119 deletions

View File

@@ -1,7 +1,7 @@
root = true
# http://editorconfig.org/
# top-most EditorConfig file
root = true
# all files
[*]
@@ -32,6 +32,7 @@ resharper_csharp_blank_lines_around_field = 0
resharper_csharp_blank_lines_around_invocable = 0
resharper_csharp_blank_lines_around_type = 0
resharper_csharp_int_align_comments = true
resharper_csharp_keep_blank_lines_in_declarations = 1
resharper_csharp_max_line_length = 368
resharper_csharp_wrap_lines = false
resharper_place_expr_accessor_on_single_line = true

View File

@@ -2,12 +2,13 @@ using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Win32;
using Netch.Models;
using Netch.Utils;
using WindowsProxy;
using Microsoft.Win32;
using Netch.Forms;
using Netch.Models;
using Netch.Servers.Socks5;
using Netch.Servers.Trojan;
using Netch.Utils;
using Netch.Utils.HttpProxyHandler;
namespace Netch.Controllers
@@ -16,7 +17,7 @@ namespace Netch.Controllers
{
public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
public PrivoxyController pPrivoxyController = new PrivoxyController();
public PrivoxyController pPrivoxyController = new();
private string prevBypass, prevHTTP, prevPAC;
private bool prevEnabled;
@@ -35,13 +36,11 @@ namespace Netch.Controllers
try
{
if (pPrivoxyController.Start(MainController.Server, mode))
{
Global.Job.AddProcess(pPrivoxyController.Instance);
}
if (mode.Type == 3)
{
if ((MainController.Server is Socks5 or Trojan) && mode.BypassChina)
if (MainController.Server is Socks5 or Trojan && mode.BypassChina)
{
//启动PAC服务器
PACServerHandle.InitPACServer("127.0.0.1");
@@ -68,35 +67,6 @@ namespace Netch.Controllers
return true;
}
private void RecordPrevious()
{
try
{
var registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
if (registry == null)
throw new Exception();
prevPAC = registry.GetValue("AutoConfigURL")?.ToString() ?? "";
prevHTTP = registry.GetValue("ProxyServer")?.ToString() ?? "";
prevBypass = registry.GetValue("ProxyOverride")?.ToString() ?? "";
prevEnabled = registry.GetValue("ProxyEnable")?.Equals(1) ?? false; // HTTP Proxy Enabled
if (prevHTTP == $"127.0.0.1:{Global.Settings.HTTPLocalPort}")
{
prevEnabled = false;
prevHTTP = "";
}
if (prevPAC != "")
prevEnabled = true;
}
catch
{
prevEnabled = false;
prevPAC = prevHTTP = prevBypass = "";
}
}
/// <summary>
/// 停止
/// </summary>
@@ -138,5 +108,34 @@ namespace Netch.Controllers
};
Task.WaitAll(tasks);
}
private void RecordPrevious()
{
try
{
var registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
if (registry == null)
throw new Exception();
prevPAC = registry.GetValue("AutoConfigURL")?.ToString() ?? "";
prevHTTP = registry.GetValue("ProxyServer")?.ToString() ?? "";
prevBypass = registry.GetValue("ProxyOverride")?.ToString() ?? "";
prevEnabled = registry.GetValue("ProxyEnable")?.Equals(1) ?? false; // HTTP Proxy Enabled
if (prevHTTP == $"127.0.0.1:{Global.Settings.HTTPLocalPort}")
{
prevEnabled = false;
prevHTTP = "";
}
if (prevPAC != "")
prevEnabled = true;
}
catch
{
prevEnabled = false;
prevPAC = prevHTTP = prevBypass = "";
}
}
}
}

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Netch.Forms;
using Netch.Models;
using Netch.Servers.Socks5;
using Netch.Utils;
@@ -13,6 +14,19 @@ namespace Netch.Controllers
{
public static class MainController
{
public static Mode Mode;
/// TCP or Both Server
public static Server Server;
private static Server _udpServer;
public static bool NttTested;
private static readonly NTTController NTTController = new();
private static IServerController _serverController;
private static IServerController _udpServerController;
public static IServerController ServerController
{
get => _serverController;
@@ -24,10 +38,6 @@ namespace Netch.Controllers
get => _udpServerController ?? _serverController;
set => _udpServerController = value;
}
public static Mode Mode;
/// TCP or Both Server
public static Server Server;
public static Server UdpServer
{
@@ -35,15 +45,8 @@ namespace Netch.Controllers
set => _udpServer = value;
}
private static Server _udpServer;
public static IModeController ModeController { get; private set; }
public static bool NttTested;
private static readonly NTTController NTTController = new NTTController();
private static IServerController _serverController;
private static IServerController _udpServerController;
/// <summary>
/// 启动
/// </summary>
@@ -57,9 +60,7 @@ namespace Netch.Controllers
Mode = mode;
if (server is Socks5 && mode.Type == 4)
{
return false;
}
// 刷新DNS缓存
NativeMethods.FlushDNSResolverCache();
@@ -88,20 +89,16 @@ namespace Netch.Controllers
if (!ModeHelper.SkipServerController(server, mode))
{
if (!await Task.Run(() => StartServer(server, mode, ref _serverController)))
{
throw new StartFailedException();
}
StatusPortInfoText.UpdateShareLan();
}
if (!await StartMode(mode))
{
throw new StartFailedException();
}
if (mode.TestNatRequired())
NatTest();
_ = Task.Run(Bandwidth.NetTraffic);
_ = Task.Run(NatTest);
return true;
}
@@ -114,7 +111,6 @@ namespace Netch.Controllers
MessageBoxX.Show(e.Message + "\n\n" + i18N.Translate("Missing File or runtime components"), owner: Global.MainForm);
break;
case StartFailedException _:
case PortInUseException _:
break;
default:
Logging.Error($"主控制器未处理异常: {e}");
@@ -139,35 +135,30 @@ namespace Netch.Controllers
controller = ServerHelper.GetUtilByTypeName(server.Type).GetController();
if (controller is Guard instanceController)
{
Utils.Utils.KillProcessByName(instanceController.MainFile);
}
PortCheckAndShowMessageBox(controller.Socks5LocalPort(), "Socks5");
if (!PortCheckAndShowMessageBox(controller.Socks5LocalPort(), "Socks5"))
return false;
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", controller.Name));
if (controller.Start(in server, mode))
{
if (controller is Guard guard)
{
if (guard.Instance != null)
{
Task.Run(() =>
{
Thread.Sleep(1000);
Global.Job.AddProcess(guard.Instance);
});
}
}
if (server is Socks5 socks5)
{
if (socks5.Auth())
UsingPorts.Add(StatusPortInfoText.Socks5Port = controller.Socks5LocalPort());
StatusPortInfoText.Socks5Port = controller.Socks5LocalPort();
}
else
{
UsingPorts.Add(StatusPortInfoText.Socks5Port = controller.Socks5LocalPort());
StatusPortInfoText.Socks5Port = controller.Socks5LocalPort();
}
return true;
@@ -181,26 +172,18 @@ namespace Netch.Controllers
ModeController = ModeHelper.GetModeControllerByType(mode.Type, out var port, out var portName, out var portType);
if (ModeController == null)
{
return true;
}
if (port != null)
{
PortCheckAndShowMessageBox((ushort) port, portName, portType);
UsingPorts.Add((ushort) port);
}
if (!PortCheckAndShowMessageBox((ushort) port, portName, portType))
return false;
Global.MainForm.StatusText(i18N.TranslateFormat("Starting {0}", ModeController.Name));
if (await Task.Run(() => ModeController.Start(mode)))
{
if (ModeController is Guard guard)
{
if (guard.Instance != null)
{
Global.Job.AddProcess(guard.Instance);
}
}
return true;
}
@@ -213,7 +196,6 @@ namespace Netch.Controllers
/// </summary>
public static async Task Stop()
{
UsingPorts.Clear();
StatusPortInfoText.Reset();
_ = Task.Run(() => NTTController.Stop());
@@ -221,28 +203,33 @@ namespace Netch.Controllers
var tasks = new[]
{
Task.Run(() => ServerController?.Stop()),
Task.Run(() => ModeController?.Stop()),
Task.Run(() => ModeController?.Stop())
};
await Task.WhenAll(tasks);
ModeController = null;
ServerController = null;
}
/// <summary>
/// 检查端口是否被占用,
/// 检查端口是否被占用,
/// 被占用则弹窗提示, 确认后抛出异常
/// </summary>
/// <param name="port">检查的端口</param>
/// <param name="portName">端口用途名称</param>
/// <param name="portType"></param>
/// <exception cref="PortInUseException"></exception>
public static void PortCheckAndShowMessageBox(ushort port, string portName, PortType portType = PortType.Both)
public static bool PortCheckAndShowMessageBox(ushort port, string portName, PortType portType = PortType.Both)
{
if (PortInUse(port, portType))
try
{
CheckPort(port, portType);
return true;
}
catch (PortInUseException)
{
MessageBoxX.Show(i18N.TranslateFormat("The {0} port is in use.", $"{portName} ({port})"));
throw new PortInUseException();
return false;
}
catch (PortReservedException)
{
MessageBoxX.Show(i18N.TranslateFormat("The {0} port is reserved by system.", $"{portName} ({port})"));
return false;
}
}
@@ -251,6 +238,8 @@ namespace Netch.Controllers
/// </summary>
public static void NatTest()
{
if (!Mode.TestNatRequired())
return;
NttTested = false;
Task.Run(() =>
{
@@ -264,7 +253,9 @@ namespace Netch.Controllers
Global.MainForm.NatTypeStatusText(result, country);
}
else
{
Global.MainForm.NatTypeStatusText(result ?? "Error");
}
NttTested = true;
});

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading.Tasks;
using Netch.Forms;
using Netch.Models;
using Netch.Servers.Shadowsocks;
using Netch.Servers.Socks5;
@@ -15,14 +16,12 @@ namespace Netch.Controllers
{
public class NFController : IModeController
{
private static readonly ServiceController NFService = new ServiceController("netfilter2");
private static readonly ServiceController NFService = new("netfilter2");
private static readonly string BinDriver = string.Empty;
private static readonly string SystemDriver = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
private static string _sysDns;
public string Name { get; } = "Redirector";
static NFController()
{
string fileName;
@@ -47,6 +46,8 @@ namespace Netch.Controllers
BinDriver = "bin\\" + fileName;
}
public string Name { get; } = "Redirector";
public bool Start(in Mode mode)
{
if (!CheckDriver())
@@ -105,8 +106,19 @@ namespace Netch.Controllers
return aio_init();
}
public void Stop()
{
Task.Run(() =>
{
if (Global.Settings.ModifySystemDNS)
//恢复系统DNS
DNS.OutboundDNS = _sysDns;
});
aio_free();
}
/// <summary>
///
/// </summary>
/// <param name="rules"></param>
/// <param name="incompatibleRule"></param>
@@ -119,7 +131,6 @@ namespace Netch.Controllers
}
/// <summary>
///
/// </summary>
/// <param name="r"></param>
/// <param name="clear"></param>
@@ -161,9 +172,7 @@ namespace Netch.Controllers
}
if (!File.Exists(SystemDriver))
{
return InstallDriver();
}
var updateFlag = false;
@@ -178,18 +187,14 @@ namespace Netch.Controllers
{
// Installed greater than Bin
if (systemResult.Major != binResult.Major)
{
// API breaking changes
updateFlag = true;
}
}
}
else
{
if (!systemFileVersion.Equals(binFileVersion))
{
updateFlag = true;
}
}
if (!updateFlag) return true;
@@ -267,18 +272,6 @@ namespace Netch.Controllers
aio_dial((int) NameList.TYPE_ADDNAME, @"NTT\.exe");
}
public void Stop()
{
Task.Run(() =>
{
if (Global.Settings.ModifySystemDNS)
//恢复系统DNS
DNS.OutboundDNS = _sysDns;
});
aio_free();
}
#region NativeMethods
private const int UdpNameListOffset = (int) NameList.TYPE_UDPTYPE - (int) NameList.TYPE_TCPTYPE;
@@ -299,7 +292,7 @@ namespace Netch.Controllers
private static extern ulong aio_getDL();
public enum NameList : int
public enum NameList
{
TYPE_FILTERLOOPBACK,
TYPE_FILTERTCP,

View File

@@ -15,7 +15,9 @@ namespace Netch.Controllers
{
public class TUNTAPController : Guard, IModeController
{
private readonly List<string> _directIPs = new();
private readonly List<string> _proxyIPs = new();
/// <summary>
/// 服务器 IP 地址
/// </summary>
@@ -24,16 +26,16 @@ namespace Netch.Controllers
/// <summary>
/// 本地 DNS 服务控制器
/// </summary>
public DNSController DNSController = new DNSController();
public DNSController DNSController = new();
public TUNTAPController()
{
StartedKeywords.Add("Running");
StoppedKeywords.AddRange(new[] {"failed", "invalid vconfig file"});
}
public override string MainFile { get; protected set; } = "tun2socks.exe";
public override string Name { get; protected set; } = "tun2socks";
public override string MainFile { get; protected set; } = "tun2socks.exe";
public bool Start(in Mode mode)
{
@@ -43,9 +45,7 @@ namespace Netch.Controllers
// 查找出口适配器
if (!Utils.Utils.SearchOutboundAdapter())
{
return false;
}
// 查找并安装 TAP 适配器
if (!SearchTapAdapter())
@@ -79,14 +79,8 @@ namespace Netch.Controllers
}
else
{
try
{
MainController.PortCheckAndShowMessageBox(53, "DNS");
}
catch
{
if (!MainController.PortCheckAndShowMessageBox(53, "DNS"))
return false;
}
if (!DNSController.Start())
{
@@ -126,10 +120,6 @@ namespace Netch.Controllers
Task.WaitAll(tasks);
}
private readonly List<string> _directIPs = new List<string>();
private readonly List<string> _proxyIPs = new List<string>();
/// <summary>
/// 设置绕行规则
/// </summary>
@@ -150,7 +140,6 @@ namespace Netch.Controllers
//处理 NAT 类型检测,由于协议的原因,无法仅通过域名确定需要代理的 IP自己记录解析了返回的 IP仅支持默认检测服务器
if (Global.Settings.STUN_Server == "stun.stunprotocol.org")
{
try
{
Logging.Info("代理 → STUN 服务器 IP");
@@ -166,23 +155,18 @@ namespace Netch.Controllers
{
Logging.Info("NAT 类型测试域名解析失败,将不会被添加到代理列表");
}
}
if (Global.Settings.TUNTAP.ProxyDNS)
{
Logging.Info("代理 → 自定义 DNS");
if (Global.Settings.TUNTAP.UseCustomDNS)
{
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"),
RouteType.TUNTAP);
}
}
break;
@@ -303,26 +287,11 @@ namespace Netch.Controllers
return true;
}
private enum RouteType
{
Outbound,
TUNTAP
}
private enum Action
{
Create,
Delete
}
private void RouteAction(Action action, in IEnumerable<string> ipNetworks, RouteType routeType,
int metric = 0)
{
foreach (var address in ipNetworks)
{
RouteAction(action, address, routeType, metric);
}
}
private bool RouteAction(Action action, in string ipNetwork, RouteType routeType, int metric = 0)
@@ -383,11 +352,22 @@ namespace Netch.Controllers
}
if (!result)
{
Logging.Warning($"Failed to {action} Route on {routeType} Adapter: {ipNetwork} metric {metric}");
}
return result;
}
private enum RouteType
{
Outbound,
TUNTAP
}
private enum Action
{
Create,
Delete
}
}
}

View File

@@ -1,13 +1,17 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Netch.Models.GitHubRelease;
using Netch.Utils;
using Newtonsoft.Json;
namespace Netch.Controllers
{
public class UpdateChecker
public static class UpdateChecker
{
public const string Owner = @"NetchX";
public const string Repo = @"Netch";
@@ -15,20 +19,20 @@ namespace Netch.Controllers
public const string Name = @"Netch";
public const string Copyright = @"Copyright © 2019 - 2020";
public const string AssemblyVersion = @"1.7.1";
public const string AssemblyVersion = @"1.7.2";
private const string Suffix = @"";
public static readonly string Version = $"{AssemblyVersion}{(string.IsNullOrEmpty(Suffix) ? "" : $"-{Suffix}")}";
public string LatestVersionNumber;
public string LatestVersionUrl;
public Release LatestRelease;
public static string LatestVersionNumber;
public static string LatestVersionUrl;
public static Release LatestRelease;
public event EventHandler NewVersionFound;
public event EventHandler NewVersionFoundFailed;
public event EventHandler NewVersionNotFound;
public static event EventHandler NewVersionFound;
public static event EventHandler NewVersionFoundFailed;
public static event EventHandler NewVersionNotFound;
public async void Check(bool isPreRelease)
public static async void Check(bool isPreRelease)
{
try
{
@@ -45,12 +49,12 @@ namespace Netch.Controllers
if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0)
{
Logging.Info("发现新版本");
NewVersionFound?.Invoke(this, new EventArgs());
NewVersionFound?.Invoke(null, new EventArgs());
}
else
{
Logging.Info("目前是最新版本");
NewVersionNotFound?.Invoke(this, new EventArgs());
NewVersionNotFound?.Invoke(null, new EventArgs());
}
}
catch (Exception e)
@@ -58,12 +62,67 @@ namespace Netch.Controllers
if (e is WebException)
Logging.Warning($"获取新版本失败: {e.Message}");
else
{
Logging.Warning(e.ToString());
NewVersionFoundFailed?.Invoke(null, new EventArgs());
}
}
public static async Task UpdateNetch(DownloadProgressChangedEventHandler onDownloadProgressChanged)
{
using WebClient client = new();
var latestVersionDownloadUrl = LatestRelease.assets[0].browser_download_url;
var tagPage = await client.DownloadStringTaskAsync(LatestVersionUrl);
var match = Regex.Match(tagPage, @"<td .*>(?<sha256>.*)</td>", RegexOptions.Singleline);
// TODO Replace with regex get basename and sha256
var fileName = Path.GetFileName(new Uri(latestVersionDownloadUrl).LocalPath);
fileName = fileName.Insert(fileName.LastIndexOf('.'), LatestVersionNumber);
var fileFullPath = Path.Combine(Global.NetchDir, "data", fileName);
var sha256 = match.Groups["sha256"].Value;
if (File.Exists(fileFullPath))
{
if (Utils.Utils.SHA256CheckSum(fileFullPath) == sha256)
{
RunUpdater();
return;
}
NewVersionFoundFailed?.Invoke(this, new EventArgs());
File.Delete(fileFullPath);
}
try
{
client.DownloadProgressChanged += onDownloadProgressChanged;
await client.DownloadFileTaskAsync(new Uri(latestVersionDownloadUrl), fileFullPath);
client.DownloadProgressChanged -= onDownloadProgressChanged;
}
catch (Exception e)
{
throw new Exception(i18N.Translate("Download Update Failed", ": ") + e.Message);
}
if (Utils.Utils.SHA256CheckSum(fileFullPath) != sha256)
throw new Exception(i18N.Translate("The downloaded file has the wrong hash"));
RunUpdater();
void RunUpdater()
{
// if debugging process stopped, debugger will kill child processes!!!!
// 调试进程结束,调试器将会杀死子进程
// uncomment if(!Debugger.isAttach) block in NetchUpdater Project's main() method and attach to NetchUpdater process to debug
// 在 NetchUpdater 项目的 main() 方法中取消注释 if!Debugger.isAttach并附加到 NetchUpdater 进程进行调试
Process.Start(new ProcessStartInfo
{
FileName = Path.Combine(Global.NetchDir, "NetchUpdater.exe"),
Arguments =
$"{Global.Settings.UDPSocketPort} \"{fileFullPath}\" \"{Global.NetchDir}\""
});
}
}
}
}
}

View File

@@ -1,127 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Controllers;
using Netch.Models;
using Netch.Utils;
namespace Netch.Forms
{
public partial class Dummy
{
}
partial class MainForm
{
private bool _isFirstCloseWindow = true;
private async void ControlFun()
{
Configuration.Save();
if (State == State.Waiting || State == State.Stopped)
{
// 服务器、模式 需选择
if (!(ServerComboBox.SelectedItem is Server server))
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
if (!(ModeComboBox.SelectedItem is Models.Mode mode))
{
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
return;
}
// 清除模式搜索框文本选择
ModeComboBox.Select(0, 0);
State = State.Starting;
if (await MainController.Start(server, mode))
{
State = State.Started;
_ = Task.Run(() => { Bandwidth.NetTraffic(); });
// 如果勾选启动后最小化
if (Global.Settings.MinimizeWhenStarted)
{
WindowState = FormWindowState.Minimized;
if (_isFirstCloseWindow)
{
// 显示提示语
NotifyTip(i18N.Translate("Netch is now minimized to the notification bar, double click this icon to restore."));
_isFirstCloseWindow = false;
}
Hide();
}
if (Global.Settings.StartedTcping)
{
// 自动检测延迟
_ = Task.Run(() =>
{
while (State == State.Started)
{
server.Test();
// 重绘 ServerComboBox
ServerComboBox.Invalidate();
Thread.Sleep(Global.Settings.StartedTcping_Interval * 1000);
}
});
}
}
else
{
State = State.Stopped;
StatusText(i18N.Translate("Start failed"));
}
}
else
{
// 停止
State = State.Stopping;
await MainController.Stop();
State = State.Stopped;
_ = Task.Run(TestServer);
}
}
public void OnBandwidthUpdated(ulong download)
{
if (InvokeRequired)
{
BeginInvoke(new Action<ulong>(OnBandwidthUpdated), download);
return;
}
try
{
UsedBandwidthLabel.Text = $"{i18N.Translate("Used", ": ")}{Bandwidth.Compute(download)}";
//UploadSpeedLabel.Text = $"↑: {Utils.Bandwidth.Compute(upload - LastUploadBandwidth)}/s";
DownloadSpeedLabel.Text = $"↑↓: {Bandwidth.Compute(download - LastDownloadBandwidth)}/s";
//LastUploadBandwidth = upload;
LastDownloadBandwidth = download;
Refresh();
}
catch
{
// ignored
}
}
/// <summary>
/// 上一次上传的流量
/// </summary>
public ulong LastUploadBandwidth;
/// <summary>
/// 上一次下载的流量
/// </summary>
public ulong LastDownloadBandwidth;
}
}

View File

@@ -39,6 +39,7 @@
this.SubscribeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.ManageSubscribeLinksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UpdateServersFromSubscribeLinksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.OptionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.OpenDirectoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CleanDNSCacheToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -170,7 +171,8 @@
//
this.SubscribeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.ManageSubscribeLinksToolStripMenuItem,
this.UpdateServersFromSubscribeLinksToolStripMenuItem});
this.UpdateServersFromSubscribeLinksToolStripMenuItem,
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem});
this.SubscribeToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.SubscribeToolStripMenuItem.Name = "SubscribeToolStripMenuItem";
this.SubscribeToolStripMenuItem.Size = new System.Drawing.Size(77, 21);
@@ -190,6 +192,13 @@
this.UpdateServersFromSubscribeLinksToolStripMenuItem.Text = "Update Servers From Subscribe Links";
this.UpdateServersFromSubscribeLinksToolStripMenuItem.Click += new System.EventHandler(this.UpdateServersFromSubscribeLinksToolStripMenuItem_Click);
//
// UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem
//
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Name = "UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem";
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Size = new System.Drawing.Size(294, 22);
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Text = "Update Servers From Subscribe Links With Proxy";
this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem.Click += new System.EventHandler(this.UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem_Click);
//
// OptionsToolStripMenuItem
//
this.OptionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
@@ -800,6 +809,7 @@
private System.Windows.Forms.ToolStripMenuItem UpdateACLToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem updateACLWithProxyToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem UpdateServersFromSubscribeLinksToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem UpdateServersFromSubscribeLinksWithProxyToolStripMenuItem;
private System.Windows.Forms.ToolStripStatusLabel UploadSpeedLabel;
private System.Windows.Forms.ToolStripStatusLabel UsedBandwidthLabel;
private System.Windows.Forms.ToolStripLabel NewVersionLabel;

View File

@@ -1,377 +0,0 @@
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Controllers;
using Netch.Forms.Mode;
using Netch.Models;
using Netch.Utils;
namespace Netch.Forms
{
partial class Dummy
{
}
partial class MainForm
{
#region MenuStrip
#region
private void ImportServersFromClipboardToolStripMenuItem_Click(object sender, EventArgs e)
{
var texts = Clipboard.GetText();
if (!string.IsNullOrWhiteSpace(texts))
{
var servers = ShareLink.ParseText(texts);
Global.Settings.Server.AddRange(servers);
NotifyTip(i18N.TranslateFormat("Import {0} server(s) form Clipboard", servers.Count));
InitServer();
Configuration.Save();
}
}
private void AddServerToolStripMenuItem_Click(object sender, EventArgs e)
{
var s = ((ToolStripMenuItem) sender).Text;
var start = s.IndexOf("[", StringComparison.Ordinal) + 1;
var end = s.IndexOf("]", start, StringComparison.Ordinal);
var result = s.Substring(start, end - start);
Hide();
ServerHelper.GetUtilByFullName(result).Create();
InitServer();
Configuration.Save();
Show();
}
#endregion
#region
private void CreateProcessModeToolStripButton_Click(object sender, EventArgs e)
{
Hide();
new Process().ShowDialog();
Show();
}
private void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e)
{
Enabled = false;
try
{
ModeHelper.Load();
InitMode();
NotifyTip(i18N.Translate("Modes have been reload"));
}
catch (Exception)
{
// ignored
}
finally
{
Enabled = true;
}
}
#endregion
#region
private void ManageSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
{
Hide();
new SubscribeForm().ShowDialog();
InitServer();
Show();
}
private async void UpdateServersFromSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
{
await UpdateServersFromSubscribe();
}
private async Task UpdateServersFromSubscribe()
{
void DisableItems(bool v)
{
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ProfileGroupBox.Enabled = ControlButton.Enabled = v;
}
if (Global.Settings.UseProxyToUpdateSubscription && ServerComboBox.SelectedIndex == -1)
Global.Settings.UseProxyToUpdateSubscription = false;
if (Global.Settings.UseProxyToUpdateSubscription && ServerComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
if (Global.Settings.SubscribeLink.Count <= 0)
{
MessageBoxX.Show(i18N.Translate("No subscription link"));
return;
}
StatusText(i18N.Translate("Starting update subscription"));
DisableItems(false);
var useProxyToUpdateSubscription = Global.Settings.UseProxyToUpdateSubscription;
try
{
string proxyServer = null;
if (useProxyToUpdateSubscription)
{
var mode = new Models.Mode
{
Remark = "ProxyUpdate",
Type = 5
};
await MainController.Start(ServerComboBox.SelectedItem as Server, mode);
proxyServer = $"http://127.0.0.1:{Global.Settings.HTTPLocalPort}";
}
await Subscription.UpdateServersAsync(proxyServer);
InitServer();
Configuration.Save();
StatusText(i18N.Translate("Subscription updated"));
}
catch (Exception)
{
// ignored
}
finally
{
if (useProxyToUpdateSubscription)
{
try
{
await MainController.Stop();
}
catch
{
// ignored
}
}
DisableItems(true);
}
}
#endregion
#region
private void CheckForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
void OnNewVersionNotFound(object o, EventArgs args)
{
_updater.NewVersionNotFound -= OnNewVersionNotFound;
NotifyTip(i18N.Translate("Already latest version"));
}
void OnNewVersionFoundFailed(object o, EventArgs args)
{
_updater.NewVersionFoundFailed -= OnNewVersionFoundFailed;
NotifyTip(i18N.Translate("New version found failed"), info: false);
}
_updater.NewVersionNotFound += OnNewVersionNotFound;
_updater.NewVersionFoundFailed += OnNewVersionFoundFailed;
CheckUpdate();
});
}
private void OpenDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
{
Utils.Utils.Open(".\\");
}
private async void CleanDNSCacheToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
await Task.Run(() =>
{
NativeMethods.FlushDNSResolverCache();
DNS.Cache.Clear();
});
NotifyTip(i18N.Translate("DNS cache cleanup succeeded"));
}
catch (Exception)
{
// ignored
}
finally
{
StatusText();
}
}
private void updateACLWithProxyToolStripMenuItem_Click(object sender, EventArgs e)
{
UpdateACL(true);
}
private void updateACLToolStripMenuItem_Click(object sender, EventArgs e)
{
UpdateACL(false);
}
private async void UpdateACL(bool useProxy)
{
if (useProxy && ServerComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
return;
}
Enabled = false;
StatusText(i18N.TranslateFormat("Updating {0}", "ACL"));
try
{
if (useProxy)
{
var mode = new Models.Mode
{
Remark = "ProxyUpdate",
Type = 5
};
State = State.Starting;
await MainController.Start(ServerComboBox.SelectedItem as Server, mode);
}
var req = WebUtil.CreateRequest(Global.Settings.ACL);
if (useProxy)
req.Proxy = new WebProxy($"http://127.0.0.1:{Global.Settings.HTTPLocalPort}");
await WebUtil.DownloadFileAsync(req, Path.Combine(Global.NetchDir, "bin\\default.acl"));
NotifyTip(i18N.Translate("ACL updated successfully"));
}
catch (Exception e)
{
NotifyTip(i18N.Translate("ACL update failed") + "\n" + e.Message, info: false);
Logging.Error("更新 ACL 失败!" + e);
}
finally
{
if (useProxy)
{
await MainController.Stop();
State = State.Stopped;
}
StatusText();
Enabled = true;
}
}
private async void updatePACToolStripMenuItem_Click(object sender, EventArgs eventArgs)
{
Enabled = false;
StatusText(i18N.TranslateFormat("Updating {0}", "PAC"));
try
{
var req = WebUtil.CreateRequest(Global.Settings.PAC);
string pac = Path.Combine(Global.NetchDir, "bin\\pac.txt");
await WebUtil.DownloadFileAsync(req, pac);
NotifyTip(i18N.Translate("PAC updated successfully"));
}
catch (Exception e)
{
NotifyTip(i18N.Translate("PAC update failed") + "\n" + e.Message, info: false);
Logging.Error("更新 PAC 失败!" + e);
}
finally
{
StatusText();
Enabled = true;
}
}
private async void UninstallServiceToolStripMenuItem_Click(object sender, EventArgs e)
{
Enabled = false;
StatusText(i18N.TranslateFormat("Uninstalling {0}", "NF Service"));
try
{
await Task.Run(() =>
{
if (NFController.UninstallDriver())
{
NotifyTip(i18N.TranslateFormat("{0} has been uninstalled", "NF Service"));
}
});
}
finally
{
StatusText();
Enabled = true;
}
}
private async void UninstallTapDriverToolStripMenuItem_Click(object sender, EventArgs e)
{
Enabled = false;
StatusText(i18N.TranslateFormat("Uninstalling {0}", "TUN/TAP driver"));
try
{
await Task.Run(TUNTAP.deltapall);
NotifyTip(i18N.TranslateFormat("{0} has been uninstalled", "TUN/TAP driver"));
}
catch (Exception exception)
{
Logging.Error($"卸载 TUN/TAP 适配器失败: {exception}");
}
finally
{
StatusText();
Enabled = true;
}
}
#endregion
/// <summary>
/// 菜单栏强制退出
/// </summary>
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Exit(true);
}
private void VersionLabel_Click(object sender, EventArgs e)
{
Utils.Utils.Open($"https://github.com/{UpdateChecker.Owner}/{UpdateChecker.Repo}/releases");
}
private void AboutToolStripButton_Click(object sender, EventArgs e)
{
Hide();
new AboutForm().ShowDialog();
Show();
}
private void fAQToolStripMenuItem_Click(object sender, EventArgs e)
{
Utils.Utils.Open($"https://netch.org/#/docs/zh-CN/faq");
}
#endregion
}
}

View File

@@ -1,106 +0,0 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Netch.Controllers;
using Netch.Utils;
namespace Netch.Forms
{
/// <summary lang="en">
/// this class is used to disable Designer <para />
/// </summary>
/// <summary lang="zh">
/// 此类用于禁用设计器
/// </summary>
[DesignerCategory("")]
public partial class Dummy
{
}
partial class MainForm
{
private readonly UpdateChecker _updater = new UpdateChecker();
private void CheckUpdate()
{
_updater.NewVersionFound += (o, args) =>
{
NotifyTip($"{i18N.Translate(@"New version available", ": ")}{_updater.LatestVersionNumber}");
NewVersionLabel.Visible = true;
};
_updater.Check(Global.Settings.CheckBetaUpdate);
}
private async void NewVersionLabel_Click(object sender, EventArgs e)
{
if (!_updater.LatestRelease.assets.Any())
{
Utils.Utils.Open(_updater.LatestVersionUrl);
return;
}
if (MessageBoxX.Show(i18N.Translate("Download and install now?"), confirm: true) != DialogResult.OK)
return;
NotifyTip(i18N.Translate("Start downloading new version"));
var latestVersionDownloadUrl = _updater.LatestRelease.assets[0].browser_download_url;
var tagPage = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(_updater.LatestVersionUrl));
var match = Regex.Match(tagPage, @"<td .*>(?<sha256>.*)</td>", RegexOptions.Singleline);
// TODO Replace with regex get basename and sha256
var fileName = Path.GetFileName(new Uri(latestVersionDownloadUrl).LocalPath);
fileName = fileName.Insert(fileName.LastIndexOf('.'), _updater.LatestVersionNumber);
var fileFullPath = Path.Combine(Global.NetchDir, "data", fileName);
var sha256 = match.Groups["sha256"].Value;
try
{
if (File.Exists(fileFullPath))
{
if (Utils.Utils.SHA256CheckSum(fileFullPath) == sha256)
{
RunUpdater();
return;
}
File.Delete(fileFullPath);
}
// TODO Replace "New Version Found" to Progress bar
await WebUtil.DownloadFileAsync(WebUtil.CreateRequest(latestVersionDownloadUrl), fileFullPath);
if (Utils.Utils.SHA256CheckSum(fileFullPath) != sha256)
{
MessageBoxX.Show("The downloaded file has the wrong hash");
return;
}
RunUpdater();
}
catch (Exception exception)
{
NotifyTip($"{i18N.Translate("Download update failed")}\n{exception.Message}");
Logging.Error($"下载更新失败 {exception}");
}
void RunUpdater()
{
// if debugging process stopped, debugger will kill child processes!!!!
// 调试进程结束,调试器将会杀死子进程
// uncomment if(!Debugger.isAttach) block in NetchUpdater Project's main() method and attach to NetchUpdater process to debug
// 在 NetchUpdater 项目的 main() 方法中取消注释 if!Debugger.isAttach并附加到 NetchUpdater 进程进行调试
Process.Start(new ProcessStartInfo
{
FileName = Path.Combine(Global.NetchDir, "NetchUpdater.exe"),
Arguments =
$"{Global.Settings.UDPSocketPort} \"{fileFullPath}\" \"{Global.NetchDir}\""
});
}
}
}
}

View File

@@ -1,187 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Models;
using Netch.Utils;
namespace Netch.Forms
{
public partial class Dummy
{
}
partial class MainForm
{
private int _configurationGroupBoxHeight;
private int _profileConfigurationHeight;
private void InitProfile()
{
// Clear
foreach (var button in ProfileButtons)
button.Dispose();
ProfileButtons.Clear();
ProfileTable.ColumnStyles.Clear();
ProfileTable.RowStyles.Clear();
var numProfile = Global.Settings.ProfileCount;
if (numProfile == 0)
{
// Hide Profile GroupBox, Change window size
configLayoutPanel.RowStyles[2].SizeType = SizeType.Percent;
configLayoutPanel.RowStyles[2].Height = 0;
ProfileGroupBox.Visible = false;
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight - _profileConfigurationHeight);
}
else
{
// Load Profiles
ProfileTable.ColumnCount = numProfile;
while (Global.Settings.Profiles.Count < numProfile)
{
Global.Settings.Profiles.Add(new Profile());
}
for (var i = 0; i < numProfile; ++i)
{
var b = new Button();
b.Click += ProfileButton_Click;
b.Dock = DockStyle.Fill;
b.Text = !Global.Settings.Profiles[i].IsDummy ? Global.Settings.Profiles[i].ProfileName : i18N.Translate("None");
ProfileTable.Controls.Add(b, i, 0);
ProfileButtons.Add(b);
}
// equal column
for (var i = 1; i <= ProfileTable.RowCount; i++)
{
ProfileTable.RowStyles.Add(new RowStyle(SizeType.Percent, 1));
}
for (var i = 1; i <= ProfileTable.ColumnCount; i++)
{
ProfileTable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 1));
}
configLayoutPanel.RowStyles[2].SizeType = SizeType.AutoSize;
ProfileGroupBox.Visible = true;
ConfigurationGroupBox.Size = new Size(ConfigurationGroupBox.Size.Width, _configurationGroupBoxHeight);
}
}
private void LoadProfile(int index)
{
var p = Global.Settings.Profiles[index];
ProfileNameText.Text = p.ProfileName;
ModeComboBox.ResetCompletionList();
if (p.IsDummy)
throw new Exception("Profile not found.");
var server = ServerComboBox.Items.Cast<Server>().FirstOrDefault(s => s.Remark.Equals(p.ServerRemark));
var mode = ModeComboBox.Items.Cast<Models.Mode>().FirstOrDefault(m => m.Remark.Equals(p.ModeRemark));
if (server == null)
{
throw new Exception("Server not found.");
}
if (mode == null)
{
throw new Exception("Mode not found.");
}
ServerComboBox.SelectedItem = server;
ModeComboBox.SelectedItem = mode;
}
private void SaveProfile(int index)
{
var selectedServer = (Server) ServerComboBox.SelectedItem;
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
var name = ProfileNameText.Text;
Global.Settings.Profiles[index] = new Profile(selectedServer, selectedMode, name);
}
private void RemoveProfile(int index)
{
Global.Settings.Profiles[index] = new Profile();
}
private List<Button> ProfileButtons = new List<Button>();
private async void ProfileButton_Click(object sender, EventArgs e)
{
var index = ProfileButtons.IndexOf((Button) sender);
if (ModifierKeys == Keys.Control)
{
if (ServerComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a server first"));
}
else if (ModeComboBox.SelectedIndex == -1)
{
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
}
else if (ProfileNameText.Text == "")
{
MessageBoxX.Show(i18N.Translate("Please enter a profile name first"));
}
else
{
SaveProfile(index);
ProfileButtons[index].Text = ProfileNameText.Text;
}
return;
}
if (Global.Settings.Profiles[index].IsDummy)
{
MessageBoxX.Show(
i18N.Translate("No saved profile here. Save a profile first by Ctrl+Click on the button"));
return;
}
if (ModifierKeys == Keys.Shift)
{
if (MessageBoxX.Show(i18N.Translate("Remove this Profile?"), confirm: true) != DialogResult.OK) return;
RemoveProfile(index);
ProfileButtons[index].Text = i18N.Translate("None");
return;
}
try
{
LoadProfile(index);
}
catch (Exception exception)
{
MessageBoxX.Show(exception.Message, LogLevel.ERROR);
return;
}
// start the profile
ControlFun();
if (State == State.Stopping || State == State.Stopped)
{
while (State != State.Stopped)
{
await Task.Delay(250);
}
ControlFun();
}
}
}
}

View File

@@ -1,175 +0,0 @@
using System;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Utils;
namespace Netch.Forms
{
public partial class Dummy
{
}
partial class MainForm
{
#region Server
private void InitServer()
{
var comboBoxInitialized = _comboBoxInitialized;
_comboBoxInitialized = false;
ServerComboBox.Items.Clear();
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
SelectLastServer();
_comboBoxInitialized = comboBoxInitialized;
}
private static void TestServer()
{
try
{
Parallel.ForEach(Global.Settings.Server, new ParallelOptions {MaxDegreeOfParallelism = 16},
server => { server.Test(); });
}
catch (Exception)
{
// ignored
}
}
public void SelectLastServer()
{
// 如果值合法,选中该位置
if (Global.Settings.ServerComboBoxSelectedIndex > 0 &&
Global.Settings.ServerComboBoxSelectedIndex < ServerComboBox.Items.Count)
{
ServerComboBox.SelectedIndex = Global.Settings.ServerComboBoxSelectedIndex;
}
// 如果值非法,且当前 ServerComboBox 中有元素,选择第一个位置
else if (ServerComboBox.Items.Count > 0)
{
ServerComboBox.SelectedIndex = 0;
}
// 如果当前 ServerComboBox 中没元素,不做处理
}
#endregion
#region Mode
public void InitMode()
{
var comboBoxInitialized = _comboBoxInitialized;
_comboBoxInitialized = false;
ModeComboBox.Items.Clear();
ModeComboBox.Items.AddRange(Global.Modes.ToArray());
ModeComboBox.Tag = null;
SelectLastMode();
_comboBoxInitialized = comboBoxInitialized;
}
public void SelectLastMode()
{
// 如果值合法,选中该位置
if (Global.Settings.ModeComboBoxSelectedIndex > 0 &&
Global.Settings.ModeComboBoxSelectedIndex < ModeComboBox.Items.Count)
{
ModeComboBox.SelectedIndex = Global.Settings.ModeComboBoxSelectedIndex;
}
// 如果值非法,且当前 ModeComboBox 中有元素,选择第一个位置
else if (ModeComboBox.Items.Count > 0)
{
ModeComboBox.SelectedIndex = 0;
}
// 如果当前 ModeComboBox 中没元素,不做处理
}
#endregion
/// <summary>
/// Init at <see cref="MainForm_Load"/>
/// </summary>
private int _eWidth;
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
try
{
if (!(sender is ComboBox cbx))
{
return;
}
// 绘制背景颜色
e.Graphics.FillRectangle(new SolidBrush(Color.White), e.Bounds);
if (e.Index < 0) return;
// 绘制 备注/名称 字符串
e.Graphics.DrawString(cbx.Items[e.Index].ToString(), cbx.Font, new SolidBrush(Color.Black), e.Bounds);
switch (cbx.Items[e.Index])
{
case Models.Server item:
{
// 计算延迟底色
SolidBrush brush;
if (item.Delay > 200)
brush = new SolidBrush(Color.Red);
else if (item.Delay > 80)
brush = new SolidBrush(Color.Yellow);
else if (item.Delay >= 0)
brush = new SolidBrush(Color.FromArgb(50, 255, 56));
else
brush = new SolidBrush(Color.Gray);
// 绘制延迟底色
e.Graphics.FillRectangle(brush, _eWidth * 9, e.Bounds.Y, _eWidth, e.Bounds.Height);
// 绘制延迟字符串
e.Graphics.DrawString(item.Delay.ToString(), cbx.Font, new SolidBrush(Color.Black),
_eWidth * 9 + _eWidth / 30, e.Bounds.Y);
break;
}
case Models.Mode item:
{
// 绘制 模式Box 底色
e.Graphics.FillRectangle(new SolidBrush(Color.Gray), _eWidth * 9, e.Bounds.Y, _eWidth,
e.Bounds.Height);
// 绘制 模式行数 字符串
e.Graphics.DrawString(item.Rule.Count.ToString(), cbx.Font, new SolidBrush(Color.Black),
_eWidth * 9 + _eWidth / 30, e.Bounds.Y);
break;
}
}
}
catch (Exception)
{
// ignored
}
}
private void AddAddServerToolStripMenuItems()
{
foreach (var serversUtil in ServerHelper.ServerUtils.Where(i => !string.IsNullOrEmpty(i.FullName)))
{
var fullName = serversUtil.FullName;
var control = new ToolStripMenuItem
{
Name = $"Add{fullName}ServerToolStripMenuItem",
Size = new Size(259, 22),
Text = i18N.TranslateFormat("Add [{0}] Server", fullName),
};
_mainFormText.Add(control.Name, new[] {"Add [{0}] Server", fullName});
control.Click += AddServerToolStripMenuItem_Click;
ServerToolStripMenuItem.DropDownItems.Add(control);
}
}
}
}

View File

@@ -1,239 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Netch.Models;
using Netch.Utils;
namespace Netch.Forms
{
public partial class Dummy
{
}
partial class MainForm
{
private bool IsWaiting => State == State.Waiting || State == State.Stopped;
private State _state = State.Waiting;
/// <summary>
/// 当前状态
/// </summary>
public State State
{
get => _state;
private set
{
void StartDisableItems(bool enabled)
{
ServerComboBox.Enabled =
ModeComboBox.Enabled =
EditModePictureBox.Enabled =
EditServerPictureBox.Enabled =
DeleteModePictureBox.Enabled =
DeleteServerPictureBox.Enabled = enabled;
// 启动需要禁用的控件
UninstallServiceToolStripMenuItem.Enabled =
UpdateACLToolStripMenuItem.Enabled =
updateACLWithProxyToolStripMenuItem.Enabled =
updatePACToolStripMenuItem.Enabled =
UpdateServersFromSubscribeLinksToolStripMenuItem.Enabled =
UninstallTapDriverToolStripMenuItem.Enabled =
ReloadModesToolStripMenuItem.Enabled = enabled;
}
_state = value;
StatusText();
switch (value)
{
case State.Waiting:
ControlButton.Enabled = true;
ControlButton.Text = i18N.Translate("Start");
break;
case State.Starting:
ControlButton.Enabled = false;
ControlButton.Text = "...";
ProfileGroupBox.Enabled = false;
StartDisableItems(false);
break;
case State.Started:
ControlButton.Enabled = true;
ControlButton.Text = i18N.Translate("Stop");
StatusTextAppend(StatusPortInfoText.Value);
ProfileGroupBox.Enabled = true;
break;
case State.Stopping:
ControlButton.Enabled = false;
ControlButton.Text = "...";
ProfileGroupBox.Enabled = false;
BandwidthState(false);
NatTypeStatusText();
break;
case State.Stopped:
ControlButton.Enabled = true;
ControlButton.Text = i18N.Translate("Start");
LastUploadBandwidth = 0;
LastDownloadBandwidth = 0;
Bandwidth.Stop();
ProfileGroupBox.Enabled = true;
StartDisableItems(true);
break;
case State.Terminating:
Dispose();
Environment.Exit(Environment.ExitCode);
return;
}
}
}
public void BandwidthState(bool state)
{
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = state;
}
public void NatTypeStatusText(string text = "", string country = "")
{
if (InvokeRequired)
{
BeginInvoke(new Action<string, string>(NatTypeStatusText), text, country);
return;
}
if (State != State.Started)
{
NatTypeStatusLabel.Text = "";
NatTypeStatusLabel.Visible = NatTypeStatusLightLabel.Visible = false;
return;
}
if (!string.IsNullOrEmpty(text))
{
NatTypeStatusLabel.Text = $"NAT{i18N.Translate(": ")}{text} {(country != string.Empty ? $"[{country}]" : "")}";
UpdateNatTypeLight(int.TryParse(text, out var natType) ? natType : -1);
}
else
{
NatTypeStatusLabel.Text = $@"NAT{i18N.Translate(": ", "Test failed")}";
}
NatTypeStatusLabel.Visible = true;
}
/// <summary>
/// 更新 NAT指示灯颜色
/// </summary>
/// <param name="natType"></param>
private void UpdateNatTypeLight(int natType = -1)
{
if (natType > 0 && natType < 5)
{
NatTypeStatusLightLabel.Visible = Global.Flags.IsWindows10Upper;
Color c;
switch (natType)
{
case 1:
c = Color.LimeGreen;
break;
case 2:
c = Color.Yellow;
break;
case 3:
c = Color.Red;
break;
case 4:
c = Color.Black;
break;
default:
c = Color.Black;
break;
}
NatTypeStatusLightLabel.ForeColor = c;
}
else
{
NatTypeStatusLightLabel.Visible = false;
}
}
/// <summary>
/// 更新状态栏文本
/// </summary>
/// <param name="text"></param>
public void StatusText(string text = null)
{
if (InvokeRequired)
{
BeginInvoke(new Action<string>(StatusText), text);
return;
}
text ??= i18N.Translate(StateExtension.GetStatusString(State));
StatusLabel.Text = i18N.Translate("Status", ": ") + text;
}
public void StatusTextAppend(string text)
{
StatusLabel.Text += text;
}
public static class StatusPortInfoText
{
private static ushort? _socks5Port;
private static ushort? _httpPort;
private static bool _shareLan;
public static ushort HttpPort
{
set => _httpPort = value;
}
public static ushort Socks5Port
{
set => _socks5Port = value;
}
public static void UpdateShareLan() => _shareLan = Global.Settings.LocalAddress != "127.0.0.1";
public static string Value
{
get
{
var strings = new List<string>();
if (_socks5Port != null)
{
strings.Add($"Socks5 {i18N.Translate("Local Port", ": ")}{_socks5Port}");
}
if (_httpPort != null)
{
strings.Add($"HTTP {i18N.Translate("Local Port", ": ")}{_httpPort}");
}
if (!strings.Any())
return string.Empty;
return $" ({(_shareLan ? i18N.Translate("Allow other Devices to connect") + " " : "")}{string.Join(" | ", strings)})";
}
}
public static void Reset()
{
_httpPort = _socks5Port = null;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,11 @@
using System;
using System.Windows.Forms;
using Netch.Models;
using Netch.Utils;
namespace Netch.Utils
namespace Netch.Forms
{
static class MessageBoxX
public static class MessageBoxX
{
/// <summary>
/// </summary>
@@ -34,11 +35,11 @@ namespace Netch.Utils
};
return MessageBox.Show(
owner: owner,
text: text,
caption: i18N.Translate(title),
buttons: confirm ? MessageBoxButtons.OKCancel : MessageBoxButtons.OK,
icon: msgIcon);
owner,
text,
i18N.Translate(title),
confirm ? MessageBoxButtons.OKCancel : MessageBoxButtons.OK,
msgIcon);
}
}
}

View File

@@ -49,9 +49,10 @@ namespace Netch.Forms
this.ResolveServerHostnameCheckBox = new System.Windows.Forms.CheckBox();
this.ProfileCountLabel = new System.Windows.Forms.Label();
this.ProfileCountTextBox = new System.Windows.Forms.TextBox();
this.TcpingAtStartedCheckBox = new System.Windows.Forms.CheckBox();
this.DetectionIntervalLabel = new System.Windows.Forms.Label();
this.DetectionIntervalTextBox = new System.Windows.Forms.TextBox();
this.StartedPingLabel = new System.Windows.Forms.Label();
this.DetectionTickLabel = new System.Windows.Forms.Label();
this.StartedPingIntervalTextBox = new System.Windows.Forms.TextBox();
this.DetectionTickTextBox = new System.Windows.Forms.TextBox();
this.STUNServerLabel = new System.Windows.Forms.Label();
this.STUN_ServerComboBox = new System.Windows.Forms.ComboBox();
this.AclLabel = new System.Windows.Forms.Label();
@@ -105,7 +106,7 @@ namespace Netch.Forms
this.RunAtStartupCheckBox = new System.Windows.Forms.CheckBox();
this.CheckUpdateWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
this.CheckBetaUpdateCheckBox = new System.Windows.Forms.CheckBox();
this.UpdateSubscribeatWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
this.UpdateServersWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
this.AioDNSTabPage = new System.Windows.Forms.TabPage();
this.AioDNSRuleRuleLabel = new System.Windows.Forms.Label();
this.AioDNSRulePathTextBox = new System.Windows.Forms.TextBox();
@@ -154,9 +155,10 @@ namespace Netch.Forms
this.GeneralTabPage.Controls.Add(this.ResolveServerHostnameCheckBox);
this.GeneralTabPage.Controls.Add(this.ProfileCountLabel);
this.GeneralTabPage.Controls.Add(this.ProfileCountTextBox);
this.GeneralTabPage.Controls.Add(this.TcpingAtStartedCheckBox);
this.GeneralTabPage.Controls.Add(this.DetectionIntervalLabel);
this.GeneralTabPage.Controls.Add(this.DetectionIntervalTextBox);
this.GeneralTabPage.Controls.Add(this.StartedPingLabel);
this.GeneralTabPage.Controls.Add(this.DetectionTickLabel);
this.GeneralTabPage.Controls.Add(this.StartedPingIntervalTextBox);
this.GeneralTabPage.Controls.Add(this.DetectionTickTextBox);
this.GeneralTabPage.Controls.Add(this.STUNServerLabel);
this.GeneralTabPage.Controls.Add(this.STUN_ServerComboBox);
this.GeneralTabPage.Controls.Add(this.AclLabel);
@@ -173,7 +175,7 @@ namespace Netch.Forms
// ServerPingTypeLabel
//
this.ServerPingTypeLabel.AutoSize = true;
this.ServerPingTypeLabel.Location = new System.Drawing.Point(217, 160);
this.ServerPingTypeLabel.Location = new System.Drawing.Point(267, 66);
this.ServerPingTypeLabel.Name = "ServerPingTypeLabel";
this.ServerPingTypeLabel.Size = new System.Drawing.Size(89, 12);
this.ServerPingTypeLabel.TabIndex = 16;
@@ -182,26 +184,24 @@ namespace Netch.Forms
// TCPingRadioBtn
//
this.TCPingRadioBtn.AutoSize = true;
this.TCPingRadioBtn.Location = new System.Drawing.Point(376, 158);
this.TCPingRadioBtn.Location = new System.Drawing.Point(332, 85);
this.TCPingRadioBtn.Name = "TCPingRadioBtn";
this.TCPingRadioBtn.Size = new System.Drawing.Size(59, 16);
this.TCPingRadioBtn.TabIndex = 15;
this.TCPingRadioBtn.TabStop = true;
this.TCPingRadioBtn.Text = "TCPing";
this.TCPingRadioBtn.UseVisualStyleBackColor = true;
this.TCPingRadioBtn.CheckedChanged += new System.EventHandler(this.TCPingRadioBtn_CheckedChanged);
//
// ICMPingRadioBtn
//
this.ICMPingRadioBtn.AutoSize = true;
this.ICMPingRadioBtn.Location = new System.Drawing.Point(312, 158);
this.ICMPingRadioBtn.Location = new System.Drawing.Point(268, 85);
this.ICMPingRadioBtn.Name = "ICMPingRadioBtn";
this.ICMPingRadioBtn.Size = new System.Drawing.Size(65, 16);
this.ICMPingRadioBtn.TabIndex = 14;
this.ICMPingRadioBtn.TabStop = true;
this.ICMPingRadioBtn.Text = "ICMPing";
this.ICMPingRadioBtn.UseVisualStyleBackColor = true;
this.ICMPingRadioBtn.CheckedChanged += new System.EventHandler(this.ICMPingRadioBtn_CheckedChanged);
//
// PortGroupBox
//
@@ -318,32 +318,39 @@ namespace Netch.Forms
this.ProfileCountTextBox.TabIndex = 4;
this.ProfileCountTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// TcpingAtStartedCheckBox
// StartedPingLabel
//
this.TcpingAtStartedCheckBox.AutoSize = true;
this.TcpingAtStartedCheckBox.Location = new System.Drawing.Point(15, 186);
this.TcpingAtStartedCheckBox.Name = "TcpingAtStartedCheckBox";
this.TcpingAtStartedCheckBox.Size = new System.Drawing.Size(156, 16);
this.TcpingAtStartedCheckBox.TabIndex = 5;
this.TcpingAtStartedCheckBox.Text = "Delay test after start";
this.TcpingAtStartedCheckBox.UseVisualStyleBackColor = true;
this.StartedPingLabel.AutoSize = true;
this.StartedPingLabel.Location = new System.Drawing.Point(12, 187);
this.StartedPingLabel.Name = "StartedPingLabel";
this.StartedPingLabel.Size = new System.Drawing.Size(137, 12);
this.StartedPingLabel.TabIndex = 5;
this.StartedPingLabel.Text = "Delay test after start";
//
// DetectionIntervalLabel
// DetectionTickLabel
//
this.DetectionIntervalLabel.AutoSize = true;
this.DetectionIntervalLabel.Location = new System.Drawing.Point(217, 187);
this.DetectionIntervalLabel.Name = "DetectionIntervalLabel";
this.DetectionIntervalLabel.Size = new System.Drawing.Size(143, 12);
this.DetectionIntervalLabel.TabIndex = 6;
this.DetectionIntervalLabel.Text = "Detection interval(sec)";
this.DetectionTickLabel.AutoSize = true;
this.DetectionTickLabel.Location = new System.Drawing.Point(225, 160);
this.DetectionTickLabel.Name = "DetectionTickLabel";
this.DetectionTickLabel.Size = new System.Drawing.Size(119, 12);
this.DetectionTickLabel.TabIndex = 6;
this.DetectionTickLabel.Text = "Detection Tick(sec)";
//
// DetectionIntervalTextBox
// StartedPingIntervalTextBox
//
this.DetectionIntervalTextBox.Location = new System.Drawing.Point(366, 184);
this.DetectionIntervalTextBox.Name = "DetectionIntervalTextBox";
this.DetectionIntervalTextBox.Size = new System.Drawing.Size(68, 21);
this.DetectionIntervalTextBox.TabIndex = 7;
this.DetectionIntervalTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
this.StartedPingIntervalTextBox.Location = new System.Drawing.Point(177, 184);
this.StartedPingIntervalTextBox.Name = "StartedPingIntervalTextBox";
this.StartedPingIntervalTextBox.Size = new System.Drawing.Size(68, 21);
this.StartedPingIntervalTextBox.TabIndex = 7;
this.StartedPingIntervalTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// DetectionTickTextBox
//
this.DetectionTickTextBox.Location = new System.Drawing.Point(366, 157);
this.DetectionTickTextBox.Name = "DetectionTickTextBox";
this.DetectionTickTextBox.Size = new System.Drawing.Size(68, 21);
this.DetectionTickTextBox.TabIndex = 7;
this.DetectionTickTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// STUNServerLabel
//
@@ -375,7 +382,7 @@ namespace Netch.Forms
//
this.AclAddrTextBox.Location = new System.Drawing.Point(120, 245);
this.AclAddrTextBox.Name = "AclAddrTextBox";
this.AclAddrTextBox.Size = new System.Drawing.Size(315, 21);
this.AclAddrTextBox.Size = new System.Drawing.Size(314, 21);
this.AclAddrTextBox.TabIndex = 11;
this.AclAddrTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
@@ -444,7 +451,6 @@ namespace Netch.Forms
this.ModifySystemDNSCheckBox.TabIndex = 0;
this.ModifySystemDNSCheckBox.Text = "Modify System DNS";
this.ModifySystemDNSCheckBox.UseVisualStyleBackColor = true;
this.ModifySystemDNSCheckBox.CheckedChanged += new System.EventHandler(this.ModifySystemDNSCheckBox_CheckedChanged);
//
// ModifiedDNSLabel
//
@@ -457,6 +463,7 @@ namespace Netch.Forms
//
// ModifiedDNSTextBox
//
this.ModifiedDNSTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.ModifySystemDNSCheckBox, "Checked", true));
this.ModifiedDNSTextBox.Location = new System.Drawing.Point(264, 14);
this.ModifiedDNSTextBox.Name = "ModifiedDNSTextBox";
this.ModifiedDNSTextBox.Size = new System.Drawing.Size(194, 21);
@@ -472,7 +479,6 @@ namespace Netch.Forms
this.RedirectorSSCheckBox.TabIndex = 0;
this.RedirectorSSCheckBox.Text = "Redirector SS";
this.RedirectorSSCheckBox.UseVisualStyleBackColor = true;
this.RedirectorSSCheckBox.CheckedChanged += new System.EventHandler(this.ModifySystemDNSCheckBox_CheckedChanged);
//
// TAPTabPage
//
@@ -569,6 +575,7 @@ namespace Netch.Forms
//
// TUNTAPDNSTextBox
//
this.TUNTAPDNSTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", this.UseCustomDNSCheckBox, "Checked", true));
this.TUNTAPDNSTextBox.Location = new System.Drawing.Point(120, 110);
this.TUNTAPDNSTextBox.Name = "TUNTAPDNSTextBox";
this.TUNTAPDNSTextBox.Size = new System.Drawing.Size(294, 21);
@@ -807,7 +814,7 @@ namespace Netch.Forms
this.OtherTabPage.Controls.Add(this.RunAtStartupCheckBox);
this.OtherTabPage.Controls.Add(this.CheckUpdateWhenOpenedCheckBox);
this.OtherTabPage.Controls.Add(this.CheckBetaUpdateCheckBox);
this.OtherTabPage.Controls.Add(this.UpdateSubscribeatWhenOpenedCheckBox);
this.OtherTabPage.Controls.Add(this.UpdateServersWhenOpenedCheckBox);
this.OtherTabPage.Location = new System.Drawing.Point(4, 25);
this.OtherTabPage.Name = "OtherTabPage";
this.OtherTabPage.Padding = new System.Windows.Forms.Padding(3);
@@ -890,16 +897,16 @@ namespace Netch.Forms
this.CheckBetaUpdateCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.CheckBetaUpdateCheckBox.UseVisualStyleBackColor = true;
//
// UpdateSubscribeatWhenOpenedCheckBox
// UpdateServersWhenOpenedCheckBox
//
this.UpdateSubscribeatWhenOpenedCheckBox.AutoSize = true;
this.UpdateSubscribeatWhenOpenedCheckBox.Location = new System.Drawing.Point(200, 94);
this.UpdateSubscribeatWhenOpenedCheckBox.Name = "UpdateSubscribeatWhenOpenedCheckBox";
this.UpdateSubscribeatWhenOpenedCheckBox.Size = new System.Drawing.Size(204, 16);
this.UpdateSubscribeatWhenOpenedCheckBox.TabIndex = 7;
this.UpdateSubscribeatWhenOpenedCheckBox.Text = "Update subscribeat when opened";
this.UpdateSubscribeatWhenOpenedCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.UpdateSubscribeatWhenOpenedCheckBox.UseVisualStyleBackColor = true;
this.UpdateServersWhenOpenedCheckBox.AutoSize = true;
this.UpdateServersWhenOpenedCheckBox.Location = new System.Drawing.Point(200, 94);
this.UpdateServersWhenOpenedCheckBox.Name = "UpdateServersWhenOpenedCheckBox";
this.UpdateServersWhenOpenedCheckBox.Size = new System.Drawing.Size(180, 16);
this.UpdateServersWhenOpenedCheckBox.TabIndex = 7;
this.UpdateServersWhenOpenedCheckBox.Text = "Update Servers when opened";
this.UpdateServersWhenOpenedCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.UpdateServersWhenOpenedCheckBox.UseVisualStyleBackColor = true;
//
// AioDNSTabPage
//
@@ -970,7 +977,7 @@ namespace Netch.Forms
//
// ControlButton
//
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.ControlButton.Anchor = ((System.Windows.Forms.AnchorStyles) ((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.ControlButton.Location = new System.Drawing.Point(397, 363);
this.ControlButton.Name = "ControlButton";
this.ControlButton.Size = new System.Drawing.Size(75, 23);
@@ -1001,7 +1008,7 @@ namespace Netch.Forms
this.ClientSize = new System.Drawing.Size(480, 400);
this.Controls.Add(this.flowLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.Name = "SettingForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
@@ -1028,8 +1035,8 @@ namespace Netch.Forms
this.flowLayoutPanel1.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
private System.Windows.Forms.TextBox StartedPingIntervalTextBox;
private System.Windows.Forms.CheckBox NoProxyForTcpCheckBox;
#endregion
@@ -1067,7 +1074,7 @@ namespace Netch.Forms
private System.Windows.Forms.Button ControlButton;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
private System.Windows.Forms.TabPage OtherTabPage;
private System.Windows.Forms.CheckBox UpdateSubscribeatWhenOpenedCheckBox;
private System.Windows.Forms.CheckBox UpdateServersWhenOpenedCheckBox;
private System.Windows.Forms.CheckBox RunAtStartupCheckBox;
private System.Windows.Forms.CheckBox MinimizeWhenStartedCheckBox;
private System.Windows.Forms.CheckBox CheckBetaUpdateCheckBox;
@@ -1079,9 +1086,9 @@ namespace Netch.Forms
private System.Windows.Forms.ComboBox LanguageComboBox;
private System.Windows.Forms.TextBox AclAddrTextBox;
private System.Windows.Forms.Label AclLabel;
private System.Windows.Forms.Label DetectionIntervalLabel;
private System.Windows.Forms.TextBox DetectionIntervalTextBox;
private System.Windows.Forms.CheckBox TcpingAtStartedCheckBox;
private System.Windows.Forms.Label DetectionTickLabel;
private System.Windows.Forms.TextBox DetectionTickTextBox;
private System.Windows.Forms.Label StartedPingLabel;
private System.Windows.Forms.Label STUNServerLabel;
private System.Windows.Forms.ComboBox STUN_ServerComboBox;
private System.Windows.Forms.Label ProfileCountLabel;

View File

@@ -1,4 +1,3 @@
using Netch.Utils;
using System;
using System.Collections.Generic;
using System.Drawing;
@@ -7,11 +6,15 @@ using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Forms;
using Netch.Utils;
namespace Netch.Forms
{
public partial class SettingForm : Form
{
private readonly Dictionary<Control, Func<string, bool>> _checkActions = new();
private readonly Dictionary<Control, Action<Control>> _saveActions = new();
public SettingForm()
{
InitializeComponent();
@@ -19,7 +22,6 @@ namespace Netch.Forms
InitValue();
}
private void SettingForm_Load(object sender, EventArgs e)
{
TUNTAPUseCustomDNSCheckBox_CheckedChanged(null, null);
@@ -59,7 +61,7 @@ namespace Netch.Forms
Global.Settings.ResolveServerHostname);
BindRadioBox(ICMPingRadioBtn,
c => Global.Settings.ServerTCPing = c,
_ => { },
!Global.Settings.ServerTCPing);
BindRadioBox(TCPingRadioBtn,
@@ -70,13 +72,14 @@ namespace Netch.Forms
i => i > -1,
i => Global.Settings.ProfileCount = i,
Global.Settings.ProfileCount);
BindCheckBox(TcpingAtStartedCheckBox,
b => Global.Settings.StartedTcping = b,
Global.Settings.StartedTcping);
BindTextBox<int>(DetectionIntervalTextBox,
BindTextBox<int>(DetectionTickTextBox,
i => i >= 0,
i => Global.Settings.StartedTcping_Interval = i,
Global.Settings.StartedTcping_Interval);
i => Global.Settings.DetectionTick = i,
Global.Settings.DetectionTick);
BindTextBox<int>(StartedPingIntervalTextBox,
_ => true,
i => Global.Settings.StartedPingInterval = i,
Global.Settings.StartedPingInterval);
InitSTUN();
@@ -97,8 +100,6 @@ namespace Netch.Forms
b => Global.Settings.ModifySystemDNS = b,
Global.Settings.ModifySystemDNS);
ModifySystemDNSCheckBox_CheckedChanged(null, null);
BindTextBox(ModifiedDNSTextBox,
s => DNS.TrySplit(s, out _, 2),
s => Global.Settings.ModifiedDNS = s,
@@ -135,7 +136,6 @@ namespace Netch.Forms
BindCheckBox(UseCustomDNSCheckBox,
b => { Global.Settings.TUNTAP.UseCustomDNS = b; },
Global.Settings.TUNTAP.UseCustomDNS);
TUNTAPUseCustomDNSCheckBox_CheckedChanged(null, null);
BindTextBox(TUNTAPDNSTextBox,
s => !UseCustomDNSCheckBox.Checked || DNS.TrySplit(s, out _, 2),
@@ -238,9 +238,9 @@ namespace Netch.Forms
b => Global.Settings.CheckBetaUpdate = b,
Global.Settings.CheckBetaUpdate);
BindCheckBox(UpdateSubscribeatWhenOpenedCheckBox,
b => Global.Settings.UpdateSubscribeatWhenOpened = b,
Global.Settings.UpdateSubscribeatWhenOpened);
BindCheckBox(UpdateServersWhenOpenedCheckBox,
b => Global.Settings.UpdateServersWhenOpened = b,
Global.Settings.UpdateServersWhenOpened);
#endregion
@@ -266,21 +266,14 @@ namespace Netch.Forms
private void TUNTAPUseCustomDNSCheckBox_CheckedChanged(object sender, EventArgs e)
{
TUNTAPDNSTextBox.Enabled = UseCustomDNSCheckBox.Checked;
if (UseCustomDNSCheckBox.Checked)
{
TUNTAPDNSTextBox.Text = Global.Settings.TUNTAP.DNS.Any()
? DNS.Join(Global.Settings.TUNTAP.DNS)
: "1.1.1.1";
}
else
{
TUNTAPDNSTextBox.Text = "AioDNS";
}
}
private void InitSTUN()
{
try
@@ -317,9 +310,7 @@ namespace Netch.Forms
}
if (!flag)
{
return;
}
#endregion
@@ -336,9 +327,7 @@ namespace Netch.Forms
stunServer = stun[0];
if (stun.Length > 1)
if (!ushort.TryParse(stun[1], out stunServerPort))
{
errFlag = true;
}
}
else
{
@@ -356,9 +345,7 @@ namespace Netch.Forms
#region Save
foreach (var pair in _saveActions)
{
pair.Value.Invoke(pair.Key);
}
Global.Settings.STUN_Server = stunServer;
Global.Settings.STUN_Server_Port = stunServerPort;
@@ -427,23 +414,12 @@ namespace Netch.Forms
private void BindCheckBox(CheckBox control, Action<bool> save, bool value)
{
control.Checked = value;
_checkActions.Add(control, s => true);
_saveActions.Add(control, c => save.Invoke(((CheckBox)c).Checked));
_saveActions.Add(control, c => save.Invoke(((CheckBox) c).Checked));
}
private void BindRadioBox(RadioButton control, Action<bool> save, bool value)
{
control.Checked = value;
_checkActions.Add(control, s => true);
_saveActions.Add(control, c => save.Invoke(((RadioButton)c).Checked));
}
private readonly Dictionary<Control, Func<string, bool>> _checkActions = new Dictionary<Control, Func<string, bool>>();
private readonly Dictionary<Control, Action<Control>> _saveActions = new Dictionary<Control, Action<Control>>();
private void ModifySystemDNSCheckBox_CheckedChanged(object sender, EventArgs e)
{
ModifiedDNSTextBox.Enabled = ModifySystemDNSCheckBox.Checked;
_saveActions.Add(control, c => save.Invoke(((RadioButton) c).Checked));
}
private void NoProxyForUdpCheckBox_CheckedChanged(object sender, EventArgs e)
@@ -455,15 +431,5 @@ namespace Netch.Forms
{
if (NoProxyForTcpCheckBox.Checked) NoProxyForUdpCheckBox.Checked = false;
}
private void ICMPingRadioBtn_CheckedChanged(object sender, EventArgs e)
{
if (ICMPingRadioBtn.Checked) TCPingRadioBtn.Checked = false;
}
private void TCPingRadioBtn_CheckedChanged(object sender, EventArgs e)
{
if (TCPingRadioBtn.Checked) ICMPingRadioBtn.Checked = false;
}
}
}

View File

@@ -32,7 +32,7 @@
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SubscribeForm));
this.AddSubscriptionBox = new System.Windows.Forms.GroupBox();
this.UserAgentTextBox = new System.Windows.Forms.TextBox();
this.ClearButton = new System.Windows.Forms.Button();
this.UnselectButton = new System.Windows.Forms.Button();
this.AddButton = new System.Windows.Forms.Button();
this.UserAgentLabel = new System.Windows.Forms.Label();
this.LinkTextBox = new System.Windows.Forms.TextBox();
@@ -48,19 +48,16 @@
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.deleteServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.CopyLinkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UseSelectedServerCheckBox = new System.Windows.Forms.CheckBox();
this.MainTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
this.ControlsPanel = new System.Windows.Forms.Panel();
this.AddSubscriptionBox.SuspendLayout();
this.pContextMenuStrip.SuspendLayout();
this.MainTableLayoutPanel.SuspendLayout();
this.ControlsPanel.SuspendLayout();
this.SuspendLayout();
//
// AddSubscriptionBox
//
this.AddSubscriptionBox.Controls.Add(this.UserAgentTextBox);
this.AddSubscriptionBox.Controls.Add(this.ClearButton);
this.AddSubscriptionBox.Controls.Add(this.UnselectButton);
this.AddSubscriptionBox.Controls.Add(this.AddButton);
this.AddSubscriptionBox.Controls.Add(this.UserAgentLabel);
this.AddSubscriptionBox.Controls.Add(this.LinkTextBox);
@@ -68,9 +65,9 @@
this.AddSubscriptionBox.Controls.Add(this.RemarkTextBox);
this.AddSubscriptionBox.Controls.Add(this.RemarkLabel);
this.AddSubscriptionBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.AddSubscriptionBox.Location = new System.Drawing.Point(8, 214);
this.AddSubscriptionBox.Location = new System.Drawing.Point(8, 248);
this.AddSubscriptionBox.Name = "AddSubscriptionBox";
this.AddSubscriptionBox.Size = new System.Drawing.Size(668, 141);
this.AddSubscriptionBox.Size = new System.Drawing.Size(668, 135);
this.AddSubscriptionBox.TabIndex = 1;
this.AddSubscriptionBox.TabStop = false;
//
@@ -81,15 +78,15 @@
this.UserAgentTextBox.Size = new System.Drawing.Size(545, 23);
this.UserAgentTextBox.TabIndex = 6;
//
// ClearButton
// UnselectButton
//
this.ClearButton.Location = new System.Drawing.Point(448, 103);
this.ClearButton.Name = "ClearButton";
this.ClearButton.Size = new System.Drawing.Size(87, 26);
this.ClearButton.TabIndex = 7;
this.ClearButton.Text = "Unselect";
this.ClearButton.UseVisualStyleBackColor = true;
this.ClearButton.Click += new System.EventHandler(this.ClearButton_Click);
this.UnselectButton.Location = new System.Drawing.Point(448, 103);
this.UnselectButton.Name = "UnselectButton";
this.UnselectButton.Size = new System.Drawing.Size(87, 26);
this.UnselectButton.TabIndex = 7;
this.UnselectButton.Text = "Unselect";
this.UnselectButton.UseVisualStyleBackColor = true;
this.UnselectButton.Click += new System.EventHandler(this.UnselectButton_Click);
//
// AddButton
//
@@ -97,7 +94,7 @@
this.AddButton.Name = "AddButton";
this.AddButton.Size = new System.Drawing.Size(113, 26);
this.AddButton.TabIndex = 7;
this.AddButton.Text = "Add / Modify";
this.AddButton.Text = "Add";
this.AddButton.UseVisualStyleBackColor = true;
this.AddButton.Click += new System.EventHandler(this.AddButton_Click);
//
@@ -156,7 +153,7 @@
this.SubscribeLinkListView.Location = new System.Drawing.Point(8, 8);
this.SubscribeLinkListView.MultiSelect = false;
this.SubscribeLinkListView.Name = "SubscribeLinkListView";
this.SubscribeLinkListView.Size = new System.Drawing.Size(668, 200);
this.SubscribeLinkListView.Size = new System.Drawing.Size(668, 234);
this.SubscribeLinkListView.TabIndex = 0;
this.SubscribeLinkListView.UseCompatibleStateImageBehavior = false;
this.SubscribeLinkListView.View = System.Windows.Forms.View.Details;
@@ -213,44 +210,23 @@
this.CopyLinkToolStripMenuItem.Text = "CopyLink";
this.CopyLinkToolStripMenuItem.Click += new System.EventHandler(this.CopyLinkToolStripMenuItem_Click);
//
// UseSelectedServerCheckBox
//
this.UseSelectedServerCheckBox.AutoSize = true;
this.UseSelectedServerCheckBox.Location = new System.Drawing.Point(3, 4);
this.UseSelectedServerCheckBox.Name = "UseSelectedServerCheckBox";
this.UseSelectedServerCheckBox.Size = new System.Drawing.Size(285, 21);
this.UseSelectedServerCheckBox.TabIndex = 9;
this.UseSelectedServerCheckBox.Text = "Use Selected Server To Update Subscription";
this.UseSelectedServerCheckBox.UseVisualStyleBackColor = true;
//
// MainTableLayoutPanel
//
this.MainTableLayoutPanel.ColumnCount = 1;
this.MainTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.MainTableLayoutPanel.Controls.Add(this.SubscribeLinkListView, 0, 0);
this.MainTableLayoutPanel.Controls.Add(this.AddSubscriptionBox, 0, 1);
this.MainTableLayoutPanel.Controls.Add(this.ControlsPanel, 0, 2);
this.MainTableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.MainTableLayoutPanel.Location = new System.Drawing.Point(0, 0);
this.MainTableLayoutPanel.Name = "MainTableLayoutPanel";
this.MainTableLayoutPanel.Padding = new System.Windows.Forms.Padding(5);
this.MainTableLayoutPanel.RowCount = 3;
this.MainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 58.35777F));
this.MainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 41.64223F));
this.MainTableLayoutPanel.RowCount = 2;
this.MainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 62.99213F));
this.MainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 37.00787F));
this.MainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F));
this.MainTableLayoutPanel.Size = new System.Drawing.Size(684, 391);
this.MainTableLayoutPanel.TabIndex = 11;
//
// ControlsPanel
//
this.ControlsPanel.Controls.Add(this.UseSelectedServerCheckBox);
this.ControlsPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.ControlsPanel.Location = new System.Drawing.Point(5, 358);
this.ControlsPanel.Margin = new System.Windows.Forms.Padding(0);
this.ControlsPanel.Name = "ControlsPanel";
this.ControlsPanel.Size = new System.Drawing.Size(674, 28);
this.ControlsPanel.TabIndex = 2;
//
// SubscribeForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
@@ -266,20 +242,16 @@
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Subscribe";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.SubscribeForm_FormClosing);
this.Load += new System.EventHandler(this.SubscribeForm_Load);
this.AddSubscriptionBox.ResumeLayout(false);
this.AddSubscriptionBox.PerformLayout();
this.pContextMenuStrip.ResumeLayout(false);
this.MainTableLayoutPanel.ResumeLayout(false);
this.ControlsPanel.ResumeLayout(false);
this.ControlsPanel.PerformLayout();
this.ResumeLayout(false);
}
private System.Windows.Forms.ColumnHeader EnableColumnHeader;
private System.Windows.Forms.Panel ControlsPanel;
private System.Windows.Forms.TableLayoutPanel MainTableLayoutPanel;
private System.Windows.Forms.Button ClearButton;
private System.Windows.Forms.Button UnselectButton;
private System.Windows.Forms.GroupBox AddSubscriptionBox;
private System.Windows.Forms.Label RemarkLabel;
private System.Windows.Forms.TextBox LinkTextBox;
@@ -295,7 +267,6 @@
private System.Windows.Forms.Label UserAgentLabel;
private System.Windows.Forms.TextBox UserAgentTextBox;
private System.Windows.Forms.ColumnHeader UserAgentHeader;
private System.Windows.Forms.CheckBox UseSelectedServerCheckBox;
#endregion

View File

@@ -8,88 +8,65 @@ namespace Netch.Forms
{
public partial class SubscribeForm : Form
{
private int _editingIndex = -1;
public SubscribeForm()
{
InitializeComponent();
}
public void InitSubscribeLink()
{
SubscribeLinkListView.Items.Clear();
foreach (var item in Global.Settings.SubscribeLink)
{
var viewItem = new ListViewItem(new[]
{
"",
item.Remark,
item.Link,
!string.IsNullOrEmpty(item.UserAgent) ? item.UserAgent : WebUtil.DefaultUserAgent
});
viewItem.Checked = item.Enable;
SubscribeLinkListView.Items.Add(viewItem);
}
}
private void SubscribeForm_Load(object sender, EventArgs e)
{
i18N.TranslateForm(this);
i18N.TranslateForm(pContextMenuStrip);
ResetEditingGroup();
if (Global.Settings.Server.Count > 0)
{
UseSelectedServerCheckBox.Enabled = true;
UseSelectedServerCheckBox.Checked = Global.Settings.UseProxyToUpdateSubscription;
}
else
{
UseSelectedServerCheckBox.Checked = false;
UseSelectedServerCheckBox.Enabled = false;
}
InitSubscribeLink();
}
private int SelectedIndex
{
get
{
if (SubscribeLinkListView.MultiSelect)
throw new Exception();
return SubscribeLinkListView.SelectedIndices.Count == 0 ? -1 : SubscribeLinkListView.SelectedIndices[0];
}
}
#region EventHandler
private void SubscribeLinkListView_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
if (SelectedIndex != -1)
pContextMenuStrip.Show(SubscribeLinkListView, e.Location);
}
/// <summary>
/// 选中/取消选中
/// </summary>
private void SubscribeLinkListView_SelectedIndexChanged(object sender, EventArgs e)
{
SetEditingGroup(SelectedIndex);
}
/// <summary>
/// 订阅启/禁用
/// </summary>
private void SubscribeLinkListView_ItemChecked(object sender, ItemCheckedEventArgs e)
{
var index = e.Item.Index;
Global.Settings.SubscribeLink[index].Enable = SubscribeLinkListView.Items[index].Checked;
}
private void SubscribeForm_FormClosing(object sender, FormClosingEventArgs e)
{
Configuration.Save();
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
}
private void CopyLinkToolStripMenuItem_Click(object sender, EventArgs e)
{
if (SubscribeLinkListView.SelectedItems.Count > 0)
{
for (var i = SubscribeLinkListView.SelectedItems.Count - 1; i >= 0; i--)
{
var item = SubscribeLinkListView.SelectedItems[i];
var link = Global.Settings.SubscribeLink[item.Index];
Clipboard.SetText(link.Link);
}
}
}
#endregion
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 (SubscribeLinkListView.SelectedItems.Count > 0)
{
for (var i = SubscribeLinkListView.SelectedItems.Count - 1; i >= 0; i--)
{
var item = SubscribeLinkListView.SelectedItems[i];
#region EditBox
DeleteServersInGroup(item.SubItems[0].Text);
Global.Settings.SubscribeLink.RemoveAt(item.Index);
SubscribeLinkListView.Items.Remove(item);
ResetEditingGroup();
}
}
}
private void UnselectButton_Click(object sender, EventArgs e)
{
ResetEditingGroup();
}
private void AddButton_Click(object sender, EventArgs e)
@@ -112,7 +89,7 @@ namespace Netch.Forms
return;
}
if (_editingIndex == -1)
if (SelectedIndex == -1)
{
if (Global.Settings.SubscribeLink.Any(link => link.Remark.Equals(RemarkTextBox.Text)))
{
@@ -130,126 +107,107 @@ namespace Netch.Forms
}
else
{
var target = Global.Settings.SubscribeLink[_editingIndex];
if (MessageBox.Show(i18N.Translate("Delete the corresponding group of items in the server list?"), i18N.Translate("Confirm"), MessageBoxButtons.YesNo) == DialogResult.Yes)
{
DeleteServersInGroup(target.Remark);
}
else
{
RenameServersGroup(target.Remark, RemarkTextBox.Text);
}
ListViewItem listViewItem = SubscribeLinkListView.Items[_editingIndex];
var subscribeLink = Global.Settings.SubscribeLink[SelectedIndex];
target.Enable = listViewItem.Checked;
target.Link = LinkTextBox.Text;
target.Remark = RemarkTextBox.Text;
target.UserAgent = UserAgentTextBox.Text;
RenameServers(subscribeLink.Remark, RemarkTextBox.Text);
subscribeLink.Link = LinkTextBox.Text;
subscribeLink.Remark = RemarkTextBox.Text;
subscribeLink.UserAgent = UserAgentTextBox.Text;
}
Configuration.Save();
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
// MessageBoxX.Show(i18N.Translate("Saved"));
ResetEditingGroup();
MessageBoxX.Show(i18N.Translate("Saved"));
InitSubscribeLink();
}
private static void DeleteServersInGroup(string group)
#endregion
#region ContextMenu
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)
return;
var subscribeLink = Global.Settings.SubscribeLink[SelectedIndex];
DeleteServers(subscribeLink.Remark);
Global.Settings.SubscribeLink.Remove(subscribeLink);
InitSubscribeLink();
}
private void deleteServerToolStripMenuItem_Click(object sender, EventArgs e)
{
if (MessageBoxX.Show(i18N.Translate("Confirm deletion?"), confirm: true) != DialogResult.OK)
return;
DeleteServers(Global.Settings.SubscribeLink[SelectedIndex].Remark);
}
private void CopyLinkToolStripMenuItem_Click(object sender, EventArgs e)
{
Clipboard.SetText(Global.Settings.SubscribeLink[SelectedIndex].Link);
}
#endregion
#region Helper
private static void DeleteServers(string group)
{
Global.Settings.Server.RemoveAll(server => server.Group == group);
}
private static void RenameServersGroup(string oldGroup, string newGroup)
private static void RenameServers(string oldGroup, string newGroup)
{
foreach (var server in Global.Settings.Server)
{
if (server.Group == oldGroup)
{
server.Group = newGroup;
}
}
foreach (var server in Global.Settings.Server.Where(server => server.Group == oldGroup))
server.Group = newGroup;
}
/// <summary>
/// 订阅列表选中节点
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SubscribeLinkListView_SelectedIndexChanged(object sender, EventArgs e)
private void InitSubscribeLink()
{
var listView = (ListView) sender;
if (listView.SelectedItems.Count == 0)
{
// 重置
ResetEditingGroup();
return;
}
_editingIndex = listView.SelectedItems[0].Index;
SubscribeLinkListView.Items.Clear();
ListViewItem target = SubscribeLinkListView.Items[_editingIndex];
AddSubscriptionBox.Text = target.SubItems[1].Text;
RemarkTextBox.Text = target.SubItems[1].Text;
LinkTextBox.Text = target.SubItems[2].Text;
UserAgentTextBox.Text = target.SubItems[3].Text;
}
private void SubscribeLinkListView_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
if (SubscribeLinkListView.SelectedItems.Count > 0)
foreach (var item in Global.Settings.SubscribeLink)
SubscribeLinkListView.Items.Add(new ListViewItem(new[]
{
pContextMenuStrip.Show(SubscribeLinkListView, e.Location);
}
}
"",
item.Remark,
item.Link,
!string.IsNullOrEmpty(item.UserAgent) ? item.UserAgent : WebUtil.DefaultUserAgent
})
{
Checked = item.Enable
});
ResetEditingGroup();
}
private void ResetEditingGroup()
{
_editingIndex = -1;
AddSubscriptionBox.Text = string.Empty;
RemarkTextBox.Text = string.Empty;
LinkTextBox.Text = string.Empty;
UserAgentTextBox.Text = WebUtil.DefaultUserAgent;
}
private void ClearButton_Click(object sender, EventArgs e)
private void SetEditingGroup(int index)
{
ResetEditingGroup();
}
private void SubscribeLinkListView_ItemChecked(object sender, ItemCheckedEventArgs e)
{
_editingIndex = e.Item.Index;
ListViewItem listViewItem = SubscribeLinkListView.Items[e.Item.Index];
AddSubscriptionBox.Text = listViewItem.SubItems[1].Text;
RemarkTextBox.Text = listViewItem.SubItems[1].Text;
LinkTextBox.Text = listViewItem.SubItems[2].Text;
UserAgentTextBox.Text = listViewItem.SubItems[3].Text;
var settingSub = Global.Settings.SubscribeLink[_editingIndex];
settingSub.Enable = listViewItem.Checked;
settingSub.Remark = listViewItem.SubItems[1].Text;
settingSub.Link = listViewItem.SubItems[2].Text;
settingSub.UserAgent = listViewItem.SubItems[3].Text;
Configuration.Save();
}
private void deleteServerToolStripMenuItem_Click(object sender, EventArgs e)
{
if (SubscribeLinkListView.SelectedItems.Count > 0)
if (index == -1)
{
var item = SubscribeLinkListView.SelectedItems[0];
if (MessageBoxX.Show(i18N.Translate("Confirm deletion?"), confirm: true) != DialogResult.OK)
return;
DeleteServersInGroup(item.SubItems[1].Text);
ResetEditingGroup();
AddButton.Text = i18N.Translate("Add");
return;
}
var item = Global.Settings.SubscribeLink[index];
AddSubscriptionBox.Text = item.Remark;
RemarkTextBox.Text = item.Remark;
LinkTextBox.Text = item.Link;
UserAgentTextBox.Text = item.UserAgent;
AddButton.Text = i18N.Translate("Modify");
}
#endregion
}
}

View File

@@ -3,60 +3,59 @@
namespace Netch.Models
{
/// <summary>
/// TUN/TAP 适配器配置类
/// TUN/TAP 适配器配置类
/// </summary>
public class TUNTAPConfig
{
/// <summary>
/// 地址
/// 地址
/// </summary>
public string Address = "10.0.236.10";
/// <summary>
/// 掩码
/// DNS
/// </summary>
public string Netmask = "255.255.255.0";
public List<string> DNS = new();
/// <summary>
/// 网关
/// 网关
/// </summary>
public string Gateway = "10.0.236.1";
/// <summary>
/// DNS
/// 掩码
/// </summary>
public List<string> DNS = new List<string>();
public string Netmask = "255.255.255.0";
/// <summary>
/// 使用自定义 DNS 设置
/// </summary>
public bool UseCustomDNS = false;
/// <summary>
/// 模式 2 下是否代理 DNS
/// 模式 2 下是否代理 DNS
/// </summary>
public bool ProxyDNS = false;
/// <summary>
/// 使用Fake DNS
/// 使用自定义 DNS 设置
/// </summary>
public bool UseCustomDNS = false;
/// <summary>
/// 使用Fake DNS
/// </summary>
public bool UseFakeDNS = false;
}
public class KcpConfig
{
public bool congestion = false;
public int downlinkCapacity = 100;
public int mtu = 1350;
public int readBufferSize = 2;
public int tti = 50;
public int uplinkCapacity = 12;
public int downlinkCapacity = 100;
public bool congestion = false;
public int readBufferSize = 2;
public int writeBufferSize = 2;
}
@@ -64,20 +63,19 @@ namespace Netch.Models
{
public bool AllowInsecure = true;
public KcpConfig KcpConfig = new KcpConfig();
public KcpConfig KcpConfig = new();
public bool UseMux = false;
}
public class AioDNSConfig
{
public string RulePath = "bin\\china_site_list";
public string ChinaDNS = "223.5.5.5";
public string OtherDNS = "1.1.1.1";
public string Protocol = "tcp";
public string RulePath = "bin\\china_site_list";
}
/// <summary>
@@ -85,35 +83,62 @@ namespace Netch.Models
/// </summary>
public class Setting
{
public V2rayConfig V2RayConfig = new V2rayConfig();
public AioDNSConfig AioDNS = new AioDNSConfig();
/// <summary>
/// 服务器选择位置
/// 服务器列表
/// </summary>
public int ServerComboBoxSelectedIndex = 0;
public readonly List<Server> Server = new();
/// <summary>
/// 模式选择位置
/// ACL规则
/// </summary>
public int ModeComboBoxSelectedIndex = 0;
public string ACL = "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/banAD.acl";
public AioDNSConfig AioDNS = new();
/// <summary>
/// 是否关闭窗口时退出
/// 是否使用DLL启动Shadowsocks
/// </summary>
public bool BootShadowsocksFromDLL = true;
/// <summary>
/// 全局绕过 IP 列表
/// </summary>
public List<string> BypassIPs = new();
/// <summary>
/// 是否检查 Beta 更新
/// </summary>
public bool CheckBetaUpdate = false;
/// <summary>
/// 是否打开软件时检查更新
/// </summary>
public bool CheckUpdateWhenOpened = true;
/// <summary>
/// 测试所有服务器心跳/秒
/// </summary>
public int DetectionTick = 10;
/// <summary>
/// 是否关闭窗口时退出
/// </summary>
public bool ExitWhenClosed = false;
/// <summary>
/// 是否退出时停止
/// HTTP 本地端口
/// </summary>
public bool StopWhenExited = false;
public ushort HTTPLocalPort = 2802;
/// <summary>
/// 是否打开软件时启动加速
/// 语言设置
/// </summary>
public bool StartWhenOpened = false;
public string Language = "System";
/// <summary>
/// HTTP 和 Socks5 本地代理地址
/// </summary>
public string LocalAddress = "127.0.0.1";
/// <summary>
/// 是否启动后自动最小化
@@ -121,29 +146,9 @@ namespace Netch.Models
public bool MinimizeWhenStarted = false;
/// <summary>
/// 是否开机启动软件
/// 模式选择位置
/// </summary>
public bool RunAtStartup = false;
/// <summary>
/// 是否打开软件时检查更新
/// </summary>
public bool CheckUpdateWhenOpened = true;
/// <summary>
/// 是否检查 Beta 更新
/// </summary>
public bool CheckBetaUpdate = false;
/// <summary>
/// 是否打开软件时更新订阅
/// </summary>
public bool UpdateSubscribeatWhenOpened = false;
/// <summary>
/// 修改系统 DNS
/// </summary>
public bool ModifySystemDNS = false;
public int ModeComboBoxSelectedIndex = 0;
/// <summary>
/// 要修改为的系统 DNS
@@ -151,19 +156,14 @@ namespace Netch.Models
public string ModifiedDNS = "1.1.1.1,8.8.8.8";
/// <summary>
/// 解析服务器主机名
/// 修改系统 DNS
/// </summary>
public bool ResolveServerHostname = false;
public bool ModifySystemDNS = false;
/// <summary>
/// 网页请求超时 毫秒
/// GFWList
/// </summary>
public int RequestTimeout = 10000;
/// <summary>
/// PAC URL
/// </summary>
public string Pac_Url = "";
public string PAC = "https://raw.githubusercontent.com/HMBSbige/Text_Translation/master/ShadowsocksR/ss_white.pac";
/// <summary>
/// PAC端口
@@ -171,64 +171,84 @@ namespace Netch.Models
public int Pac_Port = 2803;
/// <summary>
/// HTTP 本地端口
/// PAC URL
/// </summary>
public ushort HTTPLocalPort = 2802;
public string Pac_Url = "";
/// <summary>
/// Socks5 本地端口
/// 不代理TCP
/// </summary>
public ushort Socks5LocalPort = 2801;
public bool ProcessNoProxyForTcp = false;
/// <summary>
/// Redirector TCP 占用端口
/// 不代理UDP
/// </summary>
public bool ProcessNoProxyForUdp = false;
/// <summary>
/// 快捷配置数量
/// </summary>
public int ProfileCount = 4;
/// <summary>
/// 已保存的快捷配置
/// </summary>
public List<Profile> Profiles = new();
/// <summary>
/// 是否使用RDR内置SS
/// </summary>
public bool RedirectorSS = false;
/// <summary>
/// Redirector TCP 占用端口
/// </summary>
public ushort RedirectorTCPPort = 3901;
/// <summary>
/// UDP Socket 占用端口
/// 网页请求超时 毫秒
/// </summary>
public ushort UDPSocketPort = 18291;
public int RequestTimeout = 10000;
/// <summary>
/// HTTP 和 Socks5 本地代理地址
/// 解析服务器主机名
/// </summary>
public string LocalAddress = "127.0.0.1";
public bool ResolveServerHostname = false;
/// <summary>
/// TUNTAP 适配器配置
/// 是否开机启动软件
/// </summary>
public TUNTAPConfig TUNTAP = new TUNTAPConfig();
public bool RunAtStartup = false;
/// <summary>
/// 使用代理更新订阅
/// 服务器选择位置
/// </summary>
public bool UseProxyToUpdateSubscription = false;
public int ServerComboBoxSelectedIndex = 0;
/// <summary>
/// 订阅链接列表
/// 服务器测试方式 false.ICMPing true.TCPing
/// </summary>
public List<SubscribeLink> SubscribeLink = new List<SubscribeLink>();
public bool ServerTCPing = true;
/// <summary>
/// 服务器列表
/// Socks5 本地端口
/// </summary>
public readonly List<Server> Server = new List<Server>();
public ushort Socks5LocalPort = 2801;
/// <summary>
/// 全局绕过 IP 列表
/// 启动后延迟测试间隔/秒
/// </summary>
public List<string> BypassIPs = new List<string>();
public int StartedPingInterval = -1;
/// <summary>
/// 已保存的快捷配置
/// 是否打开软件时启动加速
/// </summary>
public List<Profile> Profiles = new List<Profile>();
public bool StartWhenOpened = false;
/// <summary>
/// 快捷配置数量
/// 是否退出时停止
/// </summary>
public int ProfileCount = 4;
public bool StopWhenExited = false;
/// <summary>
/// STUN测试服务器
@@ -241,53 +261,30 @@ namespace Netch.Models
public int STUN_Server_Port = 3478;
/// <summary>
/// 是否启用启动后延迟测试
/// 订阅链接列表
/// </summary>
public bool StartedTcping = false;
public List<SubscribeLink> SubscribeLink = new();
/// <summary>
/// 启动后延迟测试间隔/秒
/// TUNTAP 适配器配置
/// </summary>
public int StartedTcping_Interval = 3;
public TUNTAPConfig TUNTAP = new();
/// <summary>
/// ACL规则
/// UDP Socket 占用端口
/// </summary>
public string ACL = "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/banAD.acl";
public ushort UDPSocketPort = 18291;
/// <summary>
/// GFWList
/// 是否打开软件时更新订阅
/// </summary>
public string PAC = "https://raw.githubusercontent.com/HMBSbige/Text_Translation/master/ShadowsocksR/ss_white.pac";
public bool UpdateServersWhenOpened = false;
/// <summary>
/// 是否使用DLL启动Shadowsocks
/// 使用代理更新订阅
/// </summary>
public bool BootShadowsocksFromDLL = true;
public bool UseProxyToUpdateSubscription = false;
/// <summary>
/// 语言设置
/// </summary>
public string Language = "System";
/// <summary>
/// 服务器测试方式 false.ICMPing true.TCPing
/// </summary>
public bool ServerTCPing = true;
/// <summary>
/// 是否使用RDR内置SS
/// </summary>
public bool RedirectorSS = false;
/// <summary>
/// 不代理UDP
/// </summary>
public bool ProcessNoProxyForUdp = false;
/// <summary>
/// 不代理TCP
/// </summary>
public bool ProcessNoProxyForTcp = false;
public V2rayConfig V2RayConfig = new();
}
}

View File

@@ -13,7 +13,7 @@ namespace Netch
/// <param name="index">适配器索引</param>
/// <param name="metric">跃点数</param>
/// <returns>是否成功</returns>
[DllImport("NetchCore", CallingConvention = CallingConvention.Cdecl, EntryPoint = "CreateRoute")]
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl, EntryPoint = "CreateRoute")]
public static extern bool CreateRoute(string address, int cidr, string gateway, int index, int metric = 0);
/// <summary>
@@ -25,7 +25,7 @@ namespace Netch
/// <param name="index">适配器索引</param>
/// <param name="metric">跃点数</param>
/// <returns>是否成功</returns>
[DllImport("NetchCore", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DeleteRoute")]
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl, EntryPoint = "DeleteRoute")]
public static extern bool DeleteRoute(string address, int cidr, string gateway, int index, int metric = 0);
[DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]

View File

@@ -21,3 +21,5 @@ XCOPY /s /Y %SolutionDir%modes\mode %TargetDir%mode\ >NUL
DEL /f %TargetDir%*.config >NUL 2>&1
DEL /f %TargetDir%*.pdb >NUL 2>&1
RD /s /Q %TargetDir%x86 >NUL 2>&1
exit 0

View File

@@ -28,7 +28,7 @@
"Import Servers From Clipboard": "从剪贴板导入服务器",
"Import servers error!": "未找到可导入的链接!",
"Add [{0}] Server": "添加 [{0}] 服务器",
"Netch is now minimized to the notification bar, double click this icon to restore.": "Netch 已最小化至通知栏,双击此图标恢复窗口",
"Netch is now minimized to the notification bar, double click this icon to restore.": "Netch 已最小化至通知栏,双击此图标恢复窗口",
"New version available": "发现新版本",
"Already latest version": "已经是最新版本",
"New version found failed": "寻找新版本失败",
@@ -66,6 +66,7 @@
"Subscribe": "订阅",
"Manage Subscribe Links": "管理订阅链接",
"Update Servers From Subscribe Links": "从订阅链接更新服务器",
"Update Servers From Subscribe Links With Proxy": "使用代理从订阅链接更新服务器",
"No subscription link": "没有任何一条订阅链接",
"Updating {0}": "正在更新 {0}",
"Update {1} server(s) from {0}": "从 {0} 更新 {1} 个服务器",
@@ -126,10 +127,10 @@
"Add": "添加",
"Scan": "扫描",
"Save": "保存",
"Add / Modify": "保存/修改",
"Modify": "修改",
"Select a folder": "选择一个目录",
"Please enter an process name (xxx.exe)": "请输入一个进程名xxx.exe",
"Rule does not conform to C++ regular expression syntax": "规则不符合C ++正则表达式语法",
"Rule does not conform to C++ regular expression syntax": "规则不符合 C++ 正则表达式语法",
"Scan completed": "扫描完成",
"Mode added successfully": "模式添加成功",
"Mode updated successfully": "模式修改成功",
@@ -163,18 +164,18 @@
"Exit when closed": "关闭时退出",
"Stop when exited": "退出时停止",
"Global Bypass IPs": "全局直连 IP",
"Port value illegal. Try again.": "端口值非法。请重试",
"Port value illegal. Try again.": "端口值非法。请重试",
"Check update when opened": "打开软件时检查更新",
"Check Beta update": "检查 Beta 更新",
"Update subscribeat when opened": "自动更新订阅",
"Update Servers when opened": "打开软件时更新服务器",
"SS DLL": "SS DLL",
"Modify System DNS": "修改系统 DNS",
"No Proxy for Udp": "不代理Udp流量",
"No Proxy for Tcp": "不代理Tcp流量",
"No Proxy for Udp": "不代理 UDP 流量",
"No Proxy for Tcp": "不代理 TCP 流量",
"ProfileCount": "快捷配置数量",
"ProfileCount value illegal. Try again.": "快捷配置数值非法。请重试",
"STUN_ServerPort value illegal. Try again.": "STUN 端口数值非法。请重试",
"Detection interval value illegal. Try again.": "检测间隔值非法。请重试",
"ProfileCount value illegal. Try again.": "快捷配置数值非法。请重试",
"STUN_ServerPort value illegal. Try again.": "STUN 端口数值非法。请重试",
"Detection interval value illegal. Try again.": "检测间隔值非法。请重试",
"TUN/TAP driver is not detected. Is it installed now?": "未检测到 TUN/TAP 驱动,是否现在安装?",
"Failed to set the system proxy, it may be caused by the lack of dependent programs. Do you want to jump to Netch's official website to download dependent programs?": "设置系统代理失败,可能是缺少依赖导致,是否跳转 Netch 官网下载依赖程序?",
"Delay test after start": "启动后延迟测试",
@@ -197,6 +198,7 @@
"Unable to start? Click me to download": "无法启动?点我下载依赖",
"The {0} port is in use.": "{0} 端口已被占用",
"The {0} port is reserved by system.": "{0} 端口是系统保留端口",
"[Web Proxy] Bypass LAN": "[网页代理] 绕过局域网",
"[Non Web Proxy] Bypass LAN": "[不设置代理] 绕过局域网",
@@ -204,4 +206,4 @@
"[Web Proxy] Bypass LAN and China": "[网页代理] 绕过局域网和中国大陆",
"[Non Web Proxy] Bypass LAN and China": "[不设置代理] 绕过局域网和中国大陆",
"[TUN/TAP] Bypass LAN and China": "[TUN/TAP] 绕过局域网和中国大陆"
}
}

View File

@@ -16,12 +16,21 @@ namespace Netch.Utils
public static readonly string ModeDirectory = Path.Combine(Global.NetchDir, $"{MODE_DIR}\\");
public static string GetRelativePath(string fullName) => fullName.Substring(ModeDirectory.Length);
public static string GetFullPath(string relativeName) => Path.Combine(ModeDirectory, relativeName);
public static string GetFullPath(Mode mode) => Path.Combine(ModeDirectory, mode.RelativePath);
public static string GetRelativePath(string fullName)
{
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);
}
/// <summary>
/// 从模式文件夹读取模式并为 <see cref="Forms.MainForm.ModeComboBox"/> 绑定数据
/// 从模式文件夹读取模式并为 <see cref="Forms.MainForm.ModeComboBox" /> 绑定数据
/// </summary>
public static void Load()
{
@@ -99,16 +108,12 @@ namespace Netch.Utils
public static void WriteFile(Mode mode)
{
if (!Directory.Exists(ModeDirectory))
{
Directory.CreateDirectory(ModeDirectory);
}
var fullName = GetFullPath(mode.RelativePath ?? mode.FileName + ".txt");
if (mode.RelativePath == null && File.Exists(fullName))
{
throw new Exception("新建模式的文件名已存在,请贡献者检查代码");
}
// 写入到模式文件里
File.WriteAllText(fullName, mode.ToFileString());
@@ -131,9 +136,7 @@ namespace Netch.Utils
{
var fullName = GetFullPath(mode);
if (File.Exists(fullName))
{
File.Delete(fullName);
}
Global.Modes.Remove(mode);
Global.MainForm.InitMode();
@@ -166,6 +169,7 @@ namespace Netch.Utils
modeController = new NFController();
port = Global.Settings.RedirectorTCPPort;
portName = "Redirector TCP";
portType = PortType.TCP;
break;
case 1:
case 2:
@@ -176,6 +180,7 @@ namespace Netch.Utils
modeController = new HTTPController();
port = Global.Settings.HTTPLocalPort;
portName = "HTTP";
portType = PortType.TCP;
MainForm.StatusPortInfoText.HttpPort = (ushort) port;
break;
case 4:

View File

@@ -24,11 +24,21 @@ namespace Netch.Utils
{
try
{
if (PortHelper.PortInUse(Global.Settings.UDPSocketPort))
{
Global.Settings.UDPSocketPort = PortHelper.GetAvailablePort();
Configuration.Save();
}
const int tryLimit = 3;
var i = tryLimit;
while (i > 0)
try
{
PortHelper.CheckPort(Global.Settings.UDPSocketPort, PortType.UDP);
if (i != tryLimit)
Configuration.Save();
break;
}
catch
{
Global.Settings.UDPSocketPort = PortHelper.GetAvailablePort(PortType.UDP);
i--;
}
var data = new byte[1024];
var newsock = new UdpClient(new IPEndPoint(IPAddress.Loopback, Global.Settings.UDPSocketPort));
@@ -38,9 +48,7 @@ namespace Netch.Utils
var result = await newsock.ReceiveAsync();
data = result.Buffer;
if (Enum.TryParse<Commands>(Encoding.ASCII.GetString(data, 0, data.Length), out var command))
{
OnCalled(command);
}
}
}
catch (Exception e)
@@ -53,14 +61,12 @@ namespace Netch.Utils
{
try
{
using (var udpClient = new UdpClient(Global.Settings.UDPSocketPort))
{
udpClient.Connect(IPAddress.Loopback, Global.Settings.UDPSocketPort);
var sendBytes = Encoding.ASCII.GetBytes(command.ToString());
await udpClient.SendAsync(sendBytes, sendBytes.Length);
using var udpClient = new UdpClient(Global.Settings.UDPSocketPort);
udpClient.Connect(IPAddress.Loopback, Global.Settings.UDPSocketPort);
var sendBytes = Encoding.ASCII.GetBytes(command.ToString());
await udpClient.SendAsync(sendBytes, sendBytes.Length);
udpClient.Close();
}
udpClient.Close();
}
catch (Exception e)
{

View File

@@ -8,15 +8,16 @@ namespace Netch.Utils
{
public static class PortHelper
{
private static readonly List<ushort[]> TCPExcludedRanges = new List<ushort[]>();
private static readonly List<ushort[]> UDPExcludedRanges = new List<ushort[]>();
private static readonly List<Range> TCPReservedRanges = new();
private static readonly List<Range> UDPReservedRanges = new();
private static readonly IPGlobalProperties NetInfo = IPGlobalProperties.GetIPGlobalProperties();
static PortHelper()
{
try
{
GetExcludedPortRange(PortType.TCP, ref TCPExcludedRanges);
GetExcludedPortRange(PortType.UDP, ref UDPExcludedRanges);
GetReservedPortRange(PortType.TCP, ref TCPReservedRanges);
GetReservedPortRange(PortType.UDP, ref UDPReservedRanges);
}
catch (Exception e)
{
@@ -24,7 +25,7 @@ namespace Netch.Utils
}
}
private static void GetExcludedPortRange(PortType portType, ref List<ushort[]> targetList)
private static void GetReservedPortRange(PortType portType, ref List<Range> targetList)
{
var lines = new List<string>();
var process = new Process
@@ -48,48 +49,22 @@ namespace Netch.Utils
var splitLine = false;
foreach (var line in lines)
{
if (!splitLine)
{
if (line.StartsWith("-"))
{
splitLine = true;
}
}
else
{
if (line == string.Empty)
break;
var value = line.Trim().Split(' ').Where(s => s != string.Empty);
var value = line.Trim().Split(' ').Where(s => s != string.Empty).ToArray();
ushort port = 0;
var _ = (from s1 in value
where ushort.TryParse(s1, out port)
select port).ToArray();
targetList.Add(_);
targetList.Add(new Range(ushort.Parse(value[0]), ushort.Parse(value[1])));
}
}
}
/// <summary>
/// 检查端口是否是保留端口
/// </summary>
/// <param name="port">端口</param>
/// <param name="type">端口类型</param>
/// <returns>是否是保留端口</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
private static bool IsPortExcluded(ushort port, PortType type)
{
return type switch
{
PortType.TCP => TCPExcludedRanges.Any(range => range[0] <= port && port <= range[1]),
PortType.UDP => UDPExcludedRanges.Any(range => range[0] <= port && port <= range[1]),
PortType.Both => IsPortExcluded(port, PortType.TCP) || IsPortExcluded(port, PortType.UDP),
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
}
/// <summary>
/// 指定类型的端口是否已经被使用了
@@ -97,39 +72,101 @@ namespace Netch.Utils
/// <param name="port">端口</param>
/// <param name="type">检查端口类型</param>
/// <returns>是否被占用</returns>
public static bool PortInUse(ushort port, PortType type = PortType.Both)
public static void CheckPort(ushort port, PortType type = PortType.Both)
{
var netInfo = IPGlobalProperties.GetIPGlobalProperties();
var isTcpUsed = type != PortType.UDP &&
(IsPortExcluded(port, PortType.TCP) ||
netInfo.GetActiveTcpListeners().Any(ipEndPoint => ipEndPoint.Port == port));
var isUdpUsed = type != PortType.TCP &&
(IsPortExcluded(port, PortType.UDP) ||
netInfo.GetActiveUdpListeners().Any(ipEndPoint => ipEndPoint.Port == port));
var isPortExcluded = !UsingPorts.Contains(port);
return isPortExcluded && (isTcpUsed || isUdpUsed);
switch (type)
{
case PortType.Both:
CheckPort(port, PortType.TCP);
CheckPort(port, PortType.UDP);
break;
default:
CheckPortInUse(port, type);
CheckPortReserved(port, type);
break;
}
}
private static void CheckPortInUse(ushort port, PortType type)
{
switch (type)
{
case PortType.Both:
CheckPortInUse(port, PortType.TCP);
CheckPortInUse(port, PortType.UDP);
break;
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>
private static void CheckPortReserved(ushort port, PortType type)
{
switch (type)
{
case PortType.Both:
CheckPortReserved(port, PortType.TCP);
CheckPortReserved(port, PortType.UDP);
return;
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);
}
}
public static ushort GetAvailablePort()
public static ushort GetAvailablePort(PortType portType = PortType.Both)
{
var random = new Random();
for (ushort i = 0; i < 55535; i++)
{
var p = (ushort) random.Next(10000, 65535);
if (!PortInUse(p))
try
{
CheckPort(p, portType);
return p;
}
catch (Exception)
{
// ignored
}
}
throw new Exception("Cant Generate Available Port");
throw new Exception();
}
}
internal readonly struct Range
{
public int Start { get; }
public int End { get; }
public Range(int start, int end)
{
Start = start;
End = end;
}
/// <summary>
/// 记录Netch使用的端口
/// </summary>
public static readonly List<ushort> UsingPorts = new List<ushort>();
public bool InRange(int num)
{
return Start <= num && num <= End;
}
}
/// <summary>
@@ -145,4 +182,7 @@ namespace Netch.Utils
public class PortInUseException : Exception
{
}
public class PortReservedException : Exception
{
}
}

View File

@@ -2,6 +2,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Timers;
using Netch.Models;
using Newtonsoft.Json.Linq;
@@ -9,14 +11,79 @@ namespace Netch.Utils
{
public static class ServerHelper
{
public static readonly IEnumerable<IServerUtil> ServerUtils;
static ServerHelper()
{
var serversUtilsTypes = Assembly.GetExecutingAssembly().GetExportedTypes().Where(type => type.GetInterfaces().Contains(typeof(IServerUtil)));
ServerUtils = serversUtilsTypes.Select(t => (IServerUtil) Activator.CreateInstance(t)).OrderBy(util => util.Priority);
}
#region Delay
public static class DelayTestHelper
{
private static readonly Timer Timer;
private static bool _mux;
static DelayTestHelper()
{
Timer = new Timer
{
Interval = 10000,
AutoReset = true,
Enabled = false
};
Timer.Elapsed += (_, _) => TestAllDelay();
}
public static bool Enabled
{
get => Timer.Enabled;
set => Timer.Enabled = value;
}
public static int Interval => (int) (Timer.Interval / 1000);
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(); });
_mux = false;
TestDelayFinished?.Invoke(null, new EventArgs());
}
catch (Exception)
{
// ignored
}
}
public static void UpdateInterval()
{
var enabled = Enabled;
Timer.Stop();
if (Global.Settings.DetectionTick <= 0)
return;
Timer.Interval = Global.Settings.DetectionTick * 1000;
if (enabled)
{
Task.Run(TestAllDelay);
Timer.Start();
}
}
}
#endregion
#region Handler
public static readonly IEnumerable<IServerUtil> ServerUtils;
public static Server ParseJObject(JObject o)
{
var handle = GetUtilByTypeName((string) o["Type"]);
@@ -47,5 +114,7 @@ namespace Netch.Utils
{
return ServerUtils.FirstOrDefault(i => i.UriScheme.Any(s => s.Equals(typeName)));
}
#endregion
}
}

View File

@@ -59,7 +59,7 @@ As well, Netch avoid the restricted NAT problem caused by SSTap. You can use an
- [go-tun2socks](https://github.com/eycorsican/go-tun2socks)
- [shadowsocks-libev](https://github.com/shadowsocks/shadowsocks-libev)
- [shadowsocksr-libev](https://github.com/shadowsocksrr/shadowsocksr-libev)
- [v2ray-core](https://github.com/v2ray/v2ray-core)
- [v2ray-core](https://github.com/v2fly/v2ray-core)
- [trojan](https://github.com/trojan-gfw/trojan)
- [ACL4SSR](https://github.com/ACL4SSR/ACL4SSR)
- [PAC](https://github.com/HMBSbige/Text_Translation/blob/master/ShadowsocksR/ss_white.pac)

View File

@@ -68,7 +68,7 @@ Netch 支持多种语言,在启动时会根据系统语言选择自身语言
- [go-tun2socks](https://github.com/eycorsican/go-tun2socks)
- [shadowsocks-libev](https://github.com/shadowsocks/shadowsocks-libev)
- [shadowsocksr-libev](https://github.com/shadowsocksrr/shadowsocksr-libev)
- [v2ray-core](https://github.com/v2ray/v2ray-core)
- [v2ray-core](https://github.com/v2fly/v2ray-core)
- [trojan](https://github.com/trojan-gfw/trojan)
- [ACL4SSR](https://github.com/ACL4SSR/ACL4SSR)
- [PAC](https://github.com/HMBSbige/Text_Translation/blob/master/ShadowsocksR/ss_white.pac)