Compare commits

...

24 Commits
6.44 ... 6.47

Author SHA1 Message Date
2dust
e30c55a0af up 6.47 2024-06-24 15:34:41 +08:00
2dust
b583590a64 Update ConfigHandler.cs
https://github.com/2dust/v2rayN/pull/5264
2024-06-24 15:25:00 +08:00
OnceUponATimeInAmerica
a28c63168a Support for setting subs-group remarks (group's name) from the (optional) "remark" query parameter in the pasted subs url (#5264) 2024-06-24 15:20:16 +08:00
2dust
620422350f Improved tun mode
Enabling tun mode.
Only one core will be run when using sing-box core;
When using a non-sing-box, the sing-box will be used to start an additional front Socks service to provide a tun entry, which will then run two cores
2024-06-24 15:19:42 +08:00
2dust
b77cc3c33b Add network attribute to route rule
https://github.com/2dust/v2rayN/discussions/5256
2024-06-22 16:44:15 +08:00
2dust
71bf9b4887 up 6.46 2024-06-21 16:02:08 +08:00
2dust
9039d401da set DOTNET_EnableWriteXorExecute=0 under win10
https://github.com/2dust/v2rayN/issues/5186
2024-06-21 14:09:57 +08:00
2dust
74a0a93201 Improve and refactor the code 2024-06-21 11:15:35 +08:00
2dust
522571f0b3 Add splithttp transport for xray
https://github.com/XTLS/Xray-core/releases/tag/v1.8.15
2024-06-20 16:20:05 +08:00
Lsyx-Good
123c49c22d Adjust singbox DNS server and rule position (#5229) 2024-06-20 16:08:08 +08:00
OnceUponATimeInAmerica
1ff88d29be Extend stats reporting to support custom configs with several load-balanced outbounds in them, with tags starting with "proxy" e.g. proxy1, proxy2 (fully compatible with existing configs) (#5225) 2024-06-20 15:32:10 +08:00
Lsyx-Good
63d5a2a1be Fix bug (#5213) 2024-06-10 15:12:39 +08:00
2dust
1e9a6cb06b Refactor code 2024-06-04 09:48:04 +08:00
2dust
31748aa660 Refactor code 2024-06-03 17:50:18 +08:00
2dust
d0c6ea6a63 remove http://cachefly.cachefly.net/100mb.test 2024-06-03 14:06:59 +08:00
2dust
7ffe286a56 Up PackageReference 2024-06-03 07:37:49 +08:00
2dust
b99b30163b Bug fix 2024-06-03 07:37:26 +08:00
kirilllivanov
83b4f1e660 Fix incorrect filenames (#5122) 2024-05-18 17:11:38 +08:00
2dust
b57ba6a98b up 6.45 2024-05-18 09:53:15 +08:00
2dust
d748e6eff4 Add destOverride 2024-05-14 15:31:19 +08:00
2dust
315d4b75b2 Optimize code 2024-05-14 13:42:42 +08:00
2dust
31267cbc33 Adjust Scan screen qrcode 2024-05-12 17:07:56 +08:00
2dust
c23379b3b6 Adjust sing-box dns 2024-05-12 15:42:28 +08:00
2dust
568144d6a2 TransitionAssist DisableTransitions 2024-05-12 14:59:23 +08:00
87 changed files with 1962 additions and 1906 deletions

View File

@@ -9,9 +9,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.26.1" />
<PackageReference Include="Grpc.Net.Client" Version="2.62.0" />
<PackageReference Include="Grpc.Tools" Version="2.63.0">
<PackageReference Include="Google.Protobuf" Version="3.27.1" />
<PackageReference Include="Grpc.Net.Client" Version="2.63.0" />
<PackageReference Include="Grpc.Tools" Version="2.64.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -58,10 +58,12 @@ namespace v2rayN
Environment.Exit(0);
return;
}
//if (RuntimeInformation.ProcessArchitecture != Architecture.X86 && RuntimeInformation.ProcessArchitecture != Architecture.X64)
//{
// _config.guiItem.enableStatistics = false;
//}
//Under Win10
if (Environment.OSVersion.Version.Major < 10)
{
Environment.SetEnvironmentVariable("DOTNET_EnableWriteXorExecute", "0", EnvironmentVariableTarget.User);
}
}
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)

View File

@@ -105,9 +105,12 @@ namespace v2rayN
/// <param name="filePath"></param>
/// <param name="nullValue"></param>
/// <returns></returns>
public static int ToFile(object? obj, string filePath, bool nullValue = true)
public static int ToFile(object? obj, string? filePath, bool nullValue = true)
{
int result;
if (filePath is null)
{
return -1;
}
try
{
using FileStream file = File.Create(filePath);
@@ -119,14 +122,13 @@ namespace v2rayN
};
JsonSerializer.Serialize(file, obj, options);
result = 0;
return 0;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
result = -1;
return -1;
}
return result;
}
}
}

View File

@@ -1,6 +1,13 @@
using QRCoder;
using QRCoder.Xaml;
using System.Drawing;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;
using ZXing.Windows.Compatibility;
namespace v2rayN
{
@@ -28,5 +35,70 @@ namespace v2rayN
return null;
}
}
public static string ScanScreen(float dpiX, float dpiY)
{
try
{
var left = (int)(SystemParameters.WorkArea.Left);
var top = (int)(SystemParameters.WorkArea.Top);
var width = (int)(SystemParameters.WorkArea.Width / dpiX);
var height = (int)(SystemParameters.WorkArea.Height / dpiY);
using Bitmap fullImage = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(fullImage))
{
g.CopyFromScreen(left, top, 0, 0, fullImage.Size, CopyPixelOperation.SourceCopy);
}
int maxTry = 10;
for (int i = 0; i < maxTry; i++)
{
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
Rectangle cropRect = new(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
Bitmap target = new(width, height);
double imageScale = (double)width / (double)cropRect.Width;
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
}
BitmapLuminanceSource source = new(target);
QRCodeReader reader = new();
BinaryBitmap bitmap = new(new HybridBinarizer(source));
var result = reader.decode(bitmap);
if (result != null)
{
return result.Text;
}
else
{
BinaryBitmap bitmap2 = new(new HybridBinarizer(source.invert()));
var result2 = reader.decode(bitmap2);
if (result2 != null)
{
return result2.Text;
}
}
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return string.Empty;
}
public static Tuple<float, float> GetDpiXY(Window window)
{
IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle();
Graphics g = Graphics.FromHwnd(hWnd);
return new(96 / g.DpiX, 96 / g.DpiY);
}
}
}

View File

@@ -18,10 +18,6 @@ using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;
using ZXing.Windows.Compatibility;
namespace v2rayN
{
@@ -83,7 +79,7 @@ namespace v2rayN
/// </summary>
/// <param name="lst"></param>
/// <returns></returns>
public static string List2String(List<string> lst, bool wrap = false)
public static string List2String(List<string>? lst, bool wrap = false)
{
try
{
@@ -481,7 +477,7 @@ namespace v2rayN
/// 验证Domain地址是否合法
/// </summary>
/// <param name="domain"></param>
public static bool IsDomain(string domain)
public static bool IsDomain(string? domain)
{
//如果为空
if (IsNullOrEmpty(domain))
@@ -950,66 +946,6 @@ namespace v2rayN
#endregion TempPath
#region scan screen
public static string ScanScreen(float dpiX, float dpiY)
{
try
{
var left = (int)(SystemParameters.WorkArea.Left);
var top = (int)(SystemParameters.WorkArea.Top);
var width = (int)(SystemParameters.WorkArea.Width / dpiX);
var height = (int)(SystemParameters.WorkArea.Height / dpiY);
using Bitmap fullImage = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(fullImage))
{
g.CopyFromScreen(left, top, 0, 0, fullImage.Size, CopyPixelOperation.SourceCopy);
}
int maxTry = 10;
for (int i = 0; i < maxTry; i++)
{
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
Rectangle cropRect = new(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
Bitmap target = new(width, height);
double imageScale = (double)width / (double)cropRect.Width;
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
}
BitmapLuminanceSource source = new(target);
BinaryBitmap bitmap = new(new HybridBinarizer(source));
QRCodeReader reader = new();
Result result = reader.decode(bitmap);
if (result != null)
{
string ret = result.Text;
return ret;
}
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return string.Empty;
}
public static Tuple<float, float> GetDpiXY(Window window)
{
IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle();
Graphics g = Graphics.FromHwnd(hWnd);
return new(96 / g.DpiX, 96 / g.DpiY);
}
#endregion scan screen
#region
/// <summary>
@@ -1148,8 +1084,10 @@ namespace v2rayN
{
try
{
var sum = MD5.HashData(Encoding.UTF8.GetBytes("wintunsingbox_tun"));
var guid = new Guid(sum);
string pnputilPath = @"C:\Windows\System32\pnputil.exe";
string arg = $" /remove-device /deviceid \"wintun\"";
string arg = $$""" /remove-device "SWD\Wintun\{{{guid}}}" """;
// Try to remove the device
Process proc = new()

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace v2rayN.Enums
{
public enum EConfigType
{

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace v2rayN.Enums
{
public enum ECoreType
{

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace v2rayN.Enums
{
public enum EGlobalHotkey
{

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace v2rayN.Enums
{
public enum EInboundProtocol
{
@@ -8,6 +8,7 @@
http2,
pac,
api,
api2,
speedtest = 21
}
}

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace v2rayN.Enums
{
public enum EMove
{

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace v2rayN.Enums
{
public enum EServerColName
{

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace v2rayN.Enums
{
public enum ESpeedActionType
{

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace v2rayN.Enums
{
public enum ESysProxyType
{

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace v2rayN.Enums
{
public enum ETransport
{
@@ -6,6 +6,7 @@
kcp,
ws,
httpupgrade,
splithttp,
h2,
http,
quic,

View File

@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace v2rayN.Enums
{
public enum EViewAction
{

View File

@@ -1,4 +1,4 @@
using v2rayN.Models;
using v2rayN.Enums;
namespace v2rayN
{
@@ -23,7 +23,8 @@ namespace v2rayN
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases";
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/SagerNet/sing-{0}/rule-set/{1}.srs";
public const string SingboxRulesetUrlGeosite = @"https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-{0}.srs";
public const string SingboxRulesetUrlGeoip = @"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/srs/{0}.srs";
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
public const string ConfigFileName = "guiNConfig.json";
@@ -106,7 +107,6 @@ namespace v2rayN
@"https://speed.cloudflare.com/__down?bytes=100000000",
@"https://speed.cloudflare.com/__down?bytes=10000000",
@"http://cachefly.cachefly.net/50mb.test",
@"http://cachefly.cachefly.net/100mb.test",
@"http://cachefly.cachefly.net/10mb.test"
};
@@ -158,7 +158,7 @@ namespace v2rayN
public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" };
public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "h2", "quic", "grpc" };
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "splithttp", "h2", "quic", "grpc" };
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
public static readonly List<string> CoreTypes = new() { "v2fly", "SagerNet", "Xray", "sing_box" };
public static readonly List<string> CoreTypes4VLESS = new() { "Xray", "sing_box" };
@@ -175,6 +175,8 @@ namespace v2rayN
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" };
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" };
public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" };
public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"];
public static readonly List<string> TunMtus = new() { "1280", "1408", "1500", "9000" };
public static readonly List<string> TunStacks = new() { "gvisor", "system" };
public static readonly List<string> PresetMsgFilters = new() { "proxy", "direct", "block", "" };

View File

@@ -1,6 +1,9 @@
using System.Data;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using v2rayN.Enums;
using v2rayN.Handler.Fmt;
using v2rayN.Models;
namespace v2rayN.Handler
@@ -1069,12 +1072,12 @@ namespace v2rayN.Handler
/// 批量添加服务器
/// </summary>
/// <param name="config"></param>
/// <param name="clipboardData"></param>
/// <param name="strData"></param>
/// <param name="subid"></param>
/// <returns>成功导入的数量</returns>
private static int AddBatchServers(Config config, string clipboardData, string subid, bool isSub, List<ProfileItem> lstOriSub)
private static int AddBatchServers(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub)
{
if (Utils.IsNullOrEmpty(clipboardData))
if (Utils.IsNullOrEmpty(strData))
{
return -1;
}
@@ -1091,7 +1094,7 @@ namespace v2rayN.Handler
//Check for duplicate indexId
List<string>? lstDbIndexId = null;
List<ProfileItem> lstAdd = new();
var arrData = clipboardData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty());
var arrData = strData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty());
if (isSub)
{
arrData = arrData.Distinct();
@@ -1107,7 +1110,7 @@ namespace v2rayN.Handler
}
continue;
}
var profileItem = ShareHandler.ImportFromClipboardConfig(str, out string msg);
var profileItem = FmtHandler.ResolveConfig(str, out string msg);
if (profileItem is null)
{
continue;
@@ -1176,55 +1179,39 @@ namespace v2rayN.Handler
return countServers;
}
private static int AddBatchServers4Custom(Config config, string clipboardData, string subid, bool isSub, List<ProfileItem> lstOriSub)
private static int AddBatchServers4Custom(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub)
{
if (Utils.IsNullOrEmpty(clipboardData))
if (Utils.IsNullOrEmpty(strData))
{
return -1;
}
var subRemarks = LazyConfig.Instance.GetSubItem(subid)?.remarks;
//判断str是否包含s的任意一个字符串
static bool Contains(string str, params string[] s)
List<ProfileItem>? lstProfiles = null;
//Is sing-box array configuration
if (lstProfiles is null || lstProfiles.Count <= 0)
{
foreach (var item in s)
{
if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
lstProfiles = SingboxFmt.ResolveFullArray(strData, subRemarks);
}
//Is v2ray array configuration
var configObjects = JsonUtils.Deserialize<Object[]>(clipboardData);
if (configObjects != null && configObjects.Length > 0)
if (lstProfiles is null || lstProfiles.Count <= 0)
{
lstProfiles = V2rayFmt.ResolveFullArray(strData, subRemarks);
}
if (lstProfiles != null && lstProfiles.Count > 0)
{
if (isSub && !Utils.IsNullOrEmpty(subid))
{
RemoveServerViaSubid(config, subid, isSub);
}
int count = 0;
foreach (var configObject in configObjects)
foreach (var it in lstProfiles)
{
var objectString = JsonUtils.Serialize(configObject);
var v2rayCon = JsonUtils.Deserialize<V2rayConfig>(objectString);
if (v2rayCon?.inbounds?.Count > 0 && v2rayCon.outbounds?.Count > 0)
it.subid = subid;
it.isSub = isSub;
if (AddCustomServer(config, it, true) == 0)
{
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json");
File.WriteAllText(fileName, objectString);
var profileIt = new ProfileItem
{
coreType = ECoreType.Xray,
address = fileName,
remarks = v2rayCon.remarks ?? "v2ray_custom",
subid = subid,
isSub = isSub
};
if (AddCustomServer(config, profileIt, true) == 0)
{
count++;
}
count++;
}
}
if (count > 0)
@@ -1233,58 +1220,39 @@ namespace v2rayN.Handler
}
}
ProfileItem profileItem = new();
//Is v2ray configuration
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(clipboardData);
if (v2rayConfig?.inbounds?.Count > 0
&& v2rayConfig.outbounds?.Count > 0)
ProfileItem? profileItem = null;
//Is sing-box configuration
if (profileItem is null)
{
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json");
File.WriteAllText(fileName, clipboardData);
profileItem.coreType = ECoreType.Xray;
profileItem.address = fileName;
profileItem.remarks = v2rayConfig.remarks ?? "v2ray_custom";
profileItem = SingboxFmt.ResolveFull(strData, subRemarks);
}
//Is v2ray configuration
if (profileItem is null)
{
profileItem = V2rayFmt.ResolveFull(strData, subRemarks);
}
//Is Clash configuration
else if (Contains(clipboardData, "port", "socks-port", "proxies"))
if (profileItem is null)
{
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.yaml");
File.WriteAllText(fileName, clipboardData);
profileItem.coreType = ECoreType.mihomo;
profileItem.address = fileName;
profileItem.remarks = "clash_custom";
profileItem = ClashFmt.ResolveFull(strData, subRemarks);
}
//Is hysteria configuration
else if (Contains(clipboardData, "server", "up", "down", "listen", "<html>", "<body>"))
if (profileItem is null)
{
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json");
File.WriteAllText(fileName, clipboardData);
profileItem.coreType = ECoreType.hysteria;
profileItem.address = fileName;
profileItem.remarks = "hysteria_custom";
profileItem = Hysteria2Fmt.ResolveFull2(strData, subRemarks);
}
if (profileItem is null)
{
profileItem = Hysteria2Fmt.ResolveFull(strData, subRemarks);
}
//Is naiveproxy configuration
else if (Contains(clipboardData, "listen", "proxy", "<html>", "<body>"))
if (profileItem is null)
{
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json");
File.WriteAllText(fileName, clipboardData);
profileItem.coreType = ECoreType.naiveproxy;
profileItem.address = fileName;
profileItem.remarks = "naiveproxy_custom";
profileItem = NaiveproxyFmt.ResolveFull(strData, subRemarks);
}
//Is Other configuration
else
if (profileItem is null || Utils.IsNullOrEmpty(profileItem.address))
{
return -1;
//var fileName = Utile.GetTempPath($"{Utile.GetGUID(false)}.txt");
//File.WriteAllText(fileName, clipboardData);
//profileItem.address = fileName;
//profileItem.remarks = "other_custom";
}
if (isSub && !Utils.IsNullOrEmpty(subid))
@@ -1297,12 +1265,6 @@ namespace v2rayN.Handler
}
profileItem.subid = subid;
profileItem.isSub = isSub;
if (Utils.IsNullOrEmpty(profileItem.address))
{
return -1;
}
if (AddCustomServer(config, profileItem, true) == 0)
{
return 1;
@@ -1313,9 +1275,9 @@ namespace v2rayN.Handler
}
}
private static int AddBatchServers4SsSIP008(Config config, string clipboardData, string subid, bool isSub, List<ProfileItem> lstOriSub)
private static int AddBatchServers4SsSIP008(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub)
{
if (Utils.IsNullOrEmpty(clipboardData))
if (Utils.IsNullOrEmpty(strData))
{
return -1;
}
@@ -1325,31 +1287,12 @@ namespace v2rayN.Handler
RemoveServerViaSubid(config, subid, isSub);
}
//SsSIP008
var lstSsServer = JsonUtils.Deserialize<List<SsServer>>(clipboardData);
if (lstSsServer?.Count <= 0)
{
var ssSIP008 = JsonUtils.Deserialize<SsSIP008>(clipboardData);
if (ssSIP008?.servers?.Count > 0)
{
lstSsServer = ssSIP008.servers;
}
}
var lstSsServer = ShadowsocksFmt.ResolveSip008(strData);
if (lstSsServer?.Count > 0)
{
int counter = 0;
foreach (var it in lstSsServer)
foreach (var ssItem in lstSsServer)
{
var ssItem = new ProfileItem()
{
subid = subid,
remarks = it.remarks,
security = it.method,
id = it.password,
address = it.server,
port = Utils.ToInt(it.server_port)
};
ssItem.subid = subid;
ssItem.isSub = isSub;
if (AddShadowsocksServer(config, ssItem) == 0)
@@ -1364,7 +1307,7 @@ namespace v2rayN.Handler
return -1;
}
public static int AddBatchServers(Config config, string clipboardData, string subid, bool isSub)
public static int AddBatchServers(Config config, string strData, string subid, bool isSub)
{
List<ProfileItem>? lstOriSub = null;
if (isSub && !Utils.IsNullOrEmpty(subid))
@@ -1373,28 +1316,28 @@ namespace v2rayN.Handler
}
var counter = 0;
if (Utils.IsBase64String(clipboardData))
if (Utils.IsBase64String(strData))
{
counter = AddBatchServers(config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub);
counter = AddBatchServers(config, Utils.Base64Decode(strData), subid, isSub, lstOriSub);
}
if (counter < 1)
{
counter = AddBatchServers(config, clipboardData, subid, isSub, lstOriSub);
counter = AddBatchServers(config, strData, subid, isSub, lstOriSub);
}
if (counter < 1)
{
counter = AddBatchServers(config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub);
counter = AddBatchServers(config, Utils.Base64Decode(strData), subid, isSub, lstOriSub);
}
if (counter < 1)
{
counter = AddBatchServers4SsSIP008(config, clipboardData, subid, isSub, lstOriSub);
counter = AddBatchServers4SsSIP008(config, strData, subid, isSub, lstOriSub);
}
//maybe other sub
if (counter < 1)
{
counter = AddBatchServers4Custom(config, clipboardData, subid, isSub, lstOriSub);
counter = AddBatchServers4Custom(config, strData, subid, isSub, lstOriSub);
}
return counter;
@@ -1413,18 +1356,27 @@ namespace v2rayN.Handler
public static int AddSubItem(Config config, string url)
{
//already exists
if (SQLiteHelper.Instance.Table<SubItem>().Where(e => e.url == url).Count() > 0)
if (SQLiteHelper.Instance.Table<SubItem>().Any(e => e.url == url))
{
return 0;
}
SubItem subItem = new()
{
id = string.Empty,
remarks = "import_sub",
url = url
};
try
{
var uri = new Uri(url);
var queryVars = HttpUtility.ParseQueryString(uri.Query);
subItem.remarks = queryVars["remarks"] ?? "import_sub";
}
catch (UriFormatException)
{
return 0;
}
return AddSubItem(config, subItem);
}
@@ -1532,16 +1484,16 @@ namespace v2rayN.Handler
/// AddBatchRoutingRules
/// </summary>
/// <param name="config"></param>
/// <param name="clipboardData"></param>
/// <param name="strData"></param>
/// <returns></returns>
public static int AddBatchRoutingRules(ref RoutingItem routingItem, string clipboardData)
public static int AddBatchRoutingRules(ref RoutingItem routingItem, string strData)
{
if (Utils.IsNullOrEmpty(clipboardData))
if (Utils.IsNullOrEmpty(strData))
{
return -1;
}
var lstRules = JsonUtils.Deserialize<List<RulesItem>>(clipboardData);
var lstRules = JsonUtils.Deserialize<List<RulesItem>>(strData);
if (lstRules == null)
{
return -1;
@@ -1673,7 +1625,7 @@ namespace v2rayN.Handler
public static int InitBuiltinRouting(Config config, bool blImportAdvancedRules = false)
{
var ver = "V2-";
var ver = "V3-";
var items = LazyConfig.Instance.RoutingItems();
if (blImportAdvancedRules || items.Where(t => t.remarks.StartsWith(ver)).ToList().Count <= 0)
{

View File

@@ -1,8 +1,9 @@
using System.IO;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler
namespace v2rayN.Handler.CoreConfig
{
/// <summary>
/// Core configuration file processing class
@@ -26,7 +27,7 @@ namespace v2rayN.Handler
{
return GenerateClientCustomConfig(node, fileName, out msg);
}
else if (config.tunModeItem.enableTun || LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box)
else if (LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box)
{
var configGenSingbox = new CoreConfigSingbox(config);
if (configGenSingbox.GenerateClientConfigContent(node, out SingboxConfig? singboxConfig, out msg) != 0)
@@ -154,7 +155,7 @@ namespace v2rayN.Handler
{
if (coreType == ECoreType.sing_box)
{
if ((new CoreConfigSingbox(config)).GenerateClientSpeedtestConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0)
if (new CoreConfigSingbox(config).GenerateClientSpeedtestConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0)
{
return -1;
}
@@ -162,7 +163,7 @@ namespace v2rayN.Handler
}
else
{
if ((new CoreConfigV2ray(config)).GenerateClientSpeedtestConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0)
if (new CoreConfigV2ray(config).GenerateClientSpeedtestConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0)
{
return -1;
}

View File

@@ -1,9 +1,10 @@
using System.Net;
using System.Net.NetworkInformation;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler
namespace v2rayN.Handler.CoreConfig
{
internal class CoreConfigSingbox
{
@@ -25,6 +26,11 @@ namespace v2rayN.Handler
msg = ResUI.CheckServerSettings;
return -1;
}
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.splithttp))
{
msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
return -1;
}
msg = ResUI.InitialConfiguration;
@@ -111,9 +117,11 @@ namespace v2rayN.Handler
{
try
{
singboxConfig.inbounds.Clear();
var listen = "::";
singboxConfig.inbounds = [];
if (!_config.tunModeItem.enableTun || (_config.tunModeItem.enableTun && _config.tunModeItem.enableExInbound))
if (!_config.tunModeItem.enableTun
|| (_config.tunModeItem.enableTun && _config.tunModeItem.enableExInbound && _config.runningCoreType == ECoreType.sing_box))
{
var inbound = new Inbound4Sbox()
{
@@ -146,11 +154,11 @@ namespace v2rayN.Handler
if (_config.inbound[0].newPort4LAN)
{
var inbound3 = GetInbound(inbound, EInboundProtocol.socks2, true);
inbound3.listen = "::";
inbound3.listen = listen;
singboxConfig.inbounds.Add(inbound3);
var inbound4 = GetInbound(inbound, EInboundProtocol.http2, false);
inbound4.listen = "::";
inbound4.listen = listen;
singboxConfig.inbounds.Add(inbound4);
//auth
@@ -162,8 +170,8 @@ namespace v2rayN.Handler
}
else
{
inbound.listen = "::";
inbound2.listen = "::";
inbound.listen = listen;
inbound2.listen = listen;
}
}
}
@@ -184,7 +192,7 @@ namespace v2rayN.Handler
tunInbound.strict_route = _config.tunModeItem.strictRoute;
tunInbound.stack = _config.tunModeItem.stack;
tunInbound.sniff = _config.inbound[0].sniffingEnabled;
tunInbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled;
//tunInbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled;
if (_config.tunModeItem.enableIPv6Address == false)
{
tunInbound.inet6_address = null;
@@ -545,7 +553,7 @@ namespace v2rayN.Handler
singboxConfig.route.rules.Add(new()
{
port = [53],
network = "udp",
network = ["udp"],
outbound = dnsOutbound
});
}
@@ -661,6 +669,10 @@ namespace v2rayN.Handler
rule.port = new List<int> { Utils.ToInt(item.port) };
}
}
if (!Utils.IsNullOrEmpty(item.network))
{
rule.network = Utils.String2List(item.network);
}
if (item.protocol?.Count > 0)
{
rule.protocol = item.protocol;
@@ -810,24 +822,9 @@ namespace v2rayN.Handler
{
return 0;
}
//Add the dns of the remote server domain
if (dns4Sbox.rules is null)
{
dns4Sbox.rules = new();
}
dns4Sbox.servers.Add(new()
{
tag = "local_local",
address = "223.5.5.5",
detour = Global.DirectTag,
});
dns4Sbox.rules.Add(new()
{
server = "local_local",
outbound = "any"
});
singboxConfig.dns = dns4Sbox;
GenDnsDomains(singboxConfig);
}
catch (Exception ex)
{
@@ -836,6 +833,37 @@ namespace v2rayN.Handler
return 0;
}
private int GenDnsDomains(SingboxConfig singboxConfig)
{
var dns4Sbox = singboxConfig.dns ?? new();
dns4Sbox.servers ??= [];
dns4Sbox.rules ??= [];
var lstDomain = singboxConfig.outbounds
.Where(t => !Utils.IsNullOrEmpty(t.server) && Utils.IsDomain(t.server))
.Select(t => t.server)
.ToList();
if (lstDomain != null && lstDomain.Count > 0)
{
//var strategy = dns4Sbox.servers.Where(t => !Utils.IsNullOrEmpty(t.strategy)).Select(t => t.strategy).FirstOrDefault();
var tag = "local_local";
dns4Sbox.servers.Add(new()
{
tag = tag,
address = "223.5.5.5",
detour = Global.DirectTag,
//strategy = strategy
});
dns4Sbox.rules.Add(new()
{
server = tag,
domain = lstDomain
});
}
singboxConfig.dns = dns4Sbox;
return 0;
}
private int GenExperimental(SingboxConfig singboxConfig)
{
if (_config.guiItem.enableStatistics)
@@ -843,7 +871,7 @@ namespace v2rayN.Handler
singboxConfig.experimental ??= new Experimental4Sbox();
singboxConfig.experimental.clash_api = new Clash_Api4Sbox()
{
external_controller = $"{Global.Loopback}:{LazyConfig.Instance.StatePort}",
external_controller = $"{Global.Loopback}:{LazyConfig.Instance.StatePort2}",
};
}
@@ -925,12 +953,14 @@ namespace v2rayN.Handler
}
else
{
var geo = item.Split('-').FirstOrDefault() ?? geosite;
var value = item.Split('-').LastOrDefault();
singboxConfig.route.rule_set.Add(new()
{
type = "remote",
format = "binary",
tag = item,
url = string.Format(Global.SingboxRulesetUrl, item.StartsWith(geosite) ? geosite : geoip, item),
url = string.Format(geo.Equals(geosite) ? Global.SingboxRulesetUrlGeosite : Global.SingboxRulesetUrlGeoip, value),
download_detour = Global.ProxyTag
});
}
@@ -1076,18 +1106,18 @@ namespace v2rayN.Handler
singboxConfig.route.rules.Add(rule);
}
GenDns(new(), singboxConfig);
var dnsServer = singboxConfig.dns?.servers.FirstOrDefault();
if (dnsServer != null)
{
dnsServer.detour = singboxConfig.route.rules.LastOrDefault()?.outbound;
}
var dnsRule = singboxConfig.dns?.rules.Where(t => t.outbound != null).FirstOrDefault();
if (dnsRule != null)
{
singboxConfig.dns.rules = [];
singboxConfig.dns.rules.Add(dnsRule);
}
GenDnsDomains(singboxConfig);
//var dnsServer = singboxConfig.dns?.servers.FirstOrDefault();
//if (dnsServer != null)
//{
// dnsServer.detour = singboxConfig.route.rules.LastOrDefault()?.outbound;
//}
//var dnsRule = singboxConfig.dns?.rules.Where(t => t.outbound != null).FirstOrDefault();
//if (dnsRule != null)
//{
// singboxConfig.dns.rules = [];
// singboxConfig.dns.rules.Add(dnsRule);
//}
//msg = string.Format(ResUI.SuccessfulConfiguration"), node.getSummary());
return 0;

View File

@@ -1,9 +1,10 @@
using System.Net;
using System.Net.NetworkInformation;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler
namespace v2rayN.Handler.CoreConfig
{
internal class CoreConfigV2ray
{
@@ -98,7 +99,8 @@ namespace v2rayN.Handler
{
try
{
v2rayConfig.inbounds = new List<Inbounds4Ray>();
var listen = "0.0.0.0";
v2rayConfig.inbounds = [];
Inbounds4Ray? inbound = GetInbound(_config.inbound[0], EInboundProtocol.socks, true);
v2rayConfig.inbounds.Add(inbound);
@@ -112,11 +114,11 @@ namespace v2rayN.Handler
if (_config.inbound[0].newPort4LAN)
{
var inbound3 = GetInbound(_config.inbound[0], EInboundProtocol.socks2, true);
inbound3.listen = "0.0.0.0";
inbound3.listen = listen;
v2rayConfig.inbounds.Add(inbound3);
var inbound4 = GetInbound(_config.inbound[0], EInboundProtocol.http2, false);
inbound4.listen = "0.0.0.0";
inbound4.listen = listen;
v2rayConfig.inbounds.Add(inbound4);
//auth
@@ -131,8 +133,8 @@ namespace v2rayN.Handler
}
else
{
inbound.listen = "0.0.0.0";
inbound2.listen = "0.0.0.0";
inbound.listen = listen;
inbound2.listen = listen;
}
}
}
@@ -143,24 +145,25 @@ namespace v2rayN.Handler
return 0;
}
private Inbounds4Ray? GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
{
string result = Utils.GetEmbedText(Global.V2raySampleInbound);
if (Utils.IsNullOrEmpty(result))
{
return null;
return new();
}
var inbound = JsonUtils.Deserialize<Inbounds4Ray>(result);
if (inbound == null)
{
return null;
return new();
}
inbound.tag = protocol.ToString();
inbound.port = inItem.localPort + (int)protocol;
inbound.protocol = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString();
inbound.settings.udp = inItem.udpEnabled;
inbound.sniffing.enabled = inItem.sniffingEnabled;
inbound.sniffing.destOverride = inItem.destOverride;
inbound.sniffing.routeOnly = inItem.routeOnly;
return inbound;
@@ -217,39 +220,43 @@ namespace v2rayN.Handler
return 0;
}
private int GenRoutingUserRule(RulesItem4Ray? rules, V2rayConfig v2rayConfig)
private int GenRoutingUserRule(RulesItem4Ray? rule, V2rayConfig v2rayConfig)
{
try
{
if (rules == null)
if (rule == null)
{
return 0;
}
if (Utils.IsNullOrEmpty(rules.port))
if (Utils.IsNullOrEmpty(rule.port))
{
rules.port = null;
rule.port = null;
}
if (rules.domain?.Count == 0)
if (Utils.IsNullOrEmpty(rule.network))
{
rules.domain = null;
rule.network = null;
}
if (rules.ip?.Count == 0)
if (rule.domain?.Count == 0)
{
rules.ip = null;
rule.domain = null;
}
if (rules.protocol?.Count == 0)
if (rule.ip?.Count == 0)
{
rules.protocol = null;
rule.ip = null;
}
if (rules.inboundTag?.Count == 0)
if (rule.protocol?.Count == 0)
{
rules.inboundTag = null;
rule.protocol = null;
}
if (rule.inboundTag?.Count == 0)
{
rule.inboundTag = null;
}
var hasDomainIp = false;
if (rules.domain?.Count > 0)
if (rule.domain?.Count > 0)
{
var it = JsonUtils.DeepCopy(rules);
var it = JsonUtils.DeepCopy(rule);
it.ip = null;
it.type = "field";
for (int k = it.domain.Count - 1; k >= 0; k--)
@@ -263,9 +270,9 @@ namespace v2rayN.Handler
v2rayConfig.routing.rules.Add(it);
hasDomainIp = true;
}
if (rules.ip?.Count > 0)
if (rule.ip?.Count > 0)
{
var it = JsonUtils.DeepCopy(rules);
var it = JsonUtils.DeepCopy(rule);
it.domain = null;
it.type = "field";
v2rayConfig.routing.rules.Add(it);
@@ -273,12 +280,12 @@ namespace v2rayN.Handler
}
if (!hasDomainIp)
{
if (!Utils.IsNullOrEmpty(rules.port)
|| (rules.protocol?.Count > 0)
|| (rules.inboundTag?.Count > 0)
if (!Utils.IsNullOrEmpty(rule.port)
|| rule.protocol?.Count > 0
|| rule.inboundTag?.Count > 0
)
{
var it = JsonUtils.DeepCopy(rules);
var it = JsonUtils.DeepCopy(rule);
it.type = "field";
v2rayConfig.routing.rules.Add(it);
}
@@ -562,6 +569,7 @@ namespace v2rayN.Handler
publicKey = node.publicKey,
shortId = node.shortId,
spiderX = node.spiderX,
show = false,
};
streamSettings.realitySettings = realitySettings;
@@ -627,6 +635,25 @@ namespace v2rayN.Handler
}
streamSettings.httpupgradeSettings = httpupgradeSettings;
break;
//splithttp
case nameof(ETransport.splithttp):
SplithttpSettings4Ray splithttpSettings = new()
{
maxUploadSize = 1000000,
maxConcurrentUploads = 10
};
if (!Utils.IsNullOrEmpty(node.path))
{
splithttpSettings.path = node.path;
}
if (!Utils.IsNullOrEmpty(host))
{
splithttpSettings.host = host;
}
streamSettings.splithttpSettings = splithttpSettings;
break;
//h2
case nameof(ETransport.h2):
@@ -671,7 +698,7 @@ namespace v2rayN.Handler
{
authority = Utils.IsNullOrEmpty(host) ? null : host,
serviceName = node.path,
multiMode = (node.headerType == Global.GrpcMultiMode),
multiMode = node.headerType == Global.GrpcMultiMode,
idle_timeout = _config.grpcItem.idle_timeout,
health_check_timeout = _config.grpcItem.health_check_timeout,
permit_without_stream = _config.grpcItem.permit_without_stream,

View File

@@ -2,6 +2,8 @@
using System.IO;
using System.Reactive.Linq;
using System.Text;
using v2rayN.Enums;
using v2rayN.Handler.CoreConfig;
using v2rayN.Models;
using v2rayN.Resx;
@@ -39,6 +41,7 @@ namespace v2rayN.Handler
if (CoreConfigHandler.GenerateClientConfig(node, fileName, out string msg, out string content) != 0)
{
ShowMsg(false, msg);
return;
}
else
{
@@ -54,22 +57,22 @@ namespace v2rayN.Handler
CoreStart(node);
//In tun mode, do a delay check and restart the core
if (_config.tunModeItem.enableTun)
{
Observable.Range(1, 1)
.Delay(TimeSpan.FromSeconds(15))
.Subscribe(x =>
{
{
if (_process == null || _process.HasExited)
{
CoreStart(node);
ShowMsg(false, "Tun mode restart the core once");
Logging.SaveLog("Tun mode restart the core once");
}
}
});
}
//if (_config.tunModeItem.enableTun)
//{
// Observable.Range(1, 1)
// .Delay(TimeSpan.FromSeconds(15))
// .Subscribe(x =>
// {
// {
// if (_process == null || _process.HasExited)
// {
// CoreStart(node);
// ShowMsg(false, "Tun mode restart the core once");
// Logging.SaveLog("Tun mode restart the core once");
// }
// }
// });
//}
}
}
@@ -183,15 +186,16 @@ namespace v2rayN.Handler
ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
ECoreType coreType;
if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun)
{
coreType = ECoreType.sing_box;
}
else
{
coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
}
//ECoreType coreType;
//if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun)
//{
// coreType = ECoreType.sing_box;
//}
//else
//{
// coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
//}
var coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
_config.runningCoreType = coreType;
var coreInfo = LazyConfig.Instance.GetCoreInfo(coreType);
@@ -203,13 +207,25 @@ namespace v2rayN.Handler
}
_process = proc;
//start a socks service
//start a pre service
if (_process != null && !_process.HasExited)
{
if ((node.configType == EConfigType.Custom && node.preSocksPort > 0))
ProfileItem? itemSocks = null;
var preCoreType = ECoreType.sing_box;
if (node.configType != EConfigType.Custom && coreType != ECoreType.sing_box && _config.tunModeItem.enableTun)
{
var preCoreType = _config.tunModeItem.enableTun ? ECoreType.sing_box : ECoreType.Xray;
var itemSocks = new ProfileItem()
itemSocks = new ProfileItem()
{
coreType = preCoreType,
configType = EConfigType.Socks,
address = Global.Loopback,
port = LazyConfig.Instance.GetLocalPort(EInboundProtocol.socks)
};
}
else if ((node.configType == EConfigType.Custom && node.preSocksPort > 0))
{
preCoreType = _config.tunModeItem.enableTun ? ECoreType.sing_box : ECoreType.Xray;
itemSocks = new ProfileItem()
{
coreType = preCoreType,
configType = EConfigType.Socks,
@@ -217,6 +233,9 @@ namespace v2rayN.Handler
port = node.preSocksPort
};
_config.runningCoreType = preCoreType;
}
if (itemSocks != null)
{
string fileName2 = Utils.GetConfigPath(Global.CorePreConfigFileName);
if (CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2, out string msg2, out string configStr) == 0)
{

View File

@@ -4,7 +4,7 @@ using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Sockets;
using v2rayN.Models;
using v2rayN.Enums;
using v2rayN.Resx;
namespace v2rayN.Handler

View File

@@ -0,0 +1,203 @@
using System.Collections.Specialized;
using System.IO;
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler.Fmt
{
internal class BaseFmt
{
protected static string GetIpv6(string address)
{
if (Utils.IsIpv6(address))
{
// 检查地址是否已经被方括号包围,如果没有,则添加方括号
return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]";
}
return address; // 如果不是IPv6地址直接返回原地址
}
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
{
if (!Utils.IsNullOrEmpty(item.flow))
{
dicQuery.Add("flow", item.flow);
}
if (!Utils.IsNullOrEmpty(item.streamSecurity))
{
dicQuery.Add("security", item.streamSecurity);
}
else
{
if (securityDef != null)
{
dicQuery.Add("security", securityDef);
}
}
if (!Utils.IsNullOrEmpty(item.sni))
{
dicQuery.Add("sni", item.sni);
}
if (!Utils.IsNullOrEmpty(item.alpn))
{
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
}
if (!Utils.IsNullOrEmpty(item.fingerprint))
{
dicQuery.Add("fp", Utils.UrlEncode(item.fingerprint));
}
if (!Utils.IsNullOrEmpty(item.publicKey))
{
dicQuery.Add("pbk", Utils.UrlEncode(item.publicKey));
}
if (!Utils.IsNullOrEmpty(item.shortId))
{
dicQuery.Add("sid", Utils.UrlEncode(item.shortId));
}
if (!Utils.IsNullOrEmpty(item.spiderX))
{
dicQuery.Add("spx", Utils.UrlEncode(item.spiderX));
}
dicQuery.Add("type", !Utils.IsNullOrEmpty(item.network) ? item.network : nameof(ETransport.tcp));
switch (item.network)
{
case nameof(ETransport.tcp):
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
if (!Utils.IsNullOrEmpty(item.requestHost))
{
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
}
break;
case nameof(ETransport.kcp):
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
if (!Utils.IsNullOrEmpty(item.path))
{
dicQuery.Add("seed", Utils.UrlEncode(item.path));
}
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
case nameof(ETransport.splithttp):
if (!Utils.IsNullOrEmpty(item.requestHost))
{
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
}
if (!Utils.IsNullOrEmpty(item.path))
{
dicQuery.Add("path", Utils.UrlEncode(item.path));
}
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
dicQuery["type"] = nameof(ETransport.http);
if (!Utils.IsNullOrEmpty(item.requestHost))
{
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
}
if (!Utils.IsNullOrEmpty(item.path))
{
dicQuery.Add("path", Utils.UrlEncode(item.path));
}
break;
case nameof(ETransport.quic):
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.requestHost));
dicQuery.Add("key", Utils.UrlEncode(item.path));
break;
case nameof(ETransport.grpc):
if (!Utils.IsNullOrEmpty(item.path))
{
dicQuery.Add("authority", Utils.UrlEncode(item.requestHost));
dicQuery.Add("serviceName", Utils.UrlEncode(item.path));
if (item.headerType is Global.GrpcGunMode or Global.GrpcMultiMode)
{
dicQuery.Add("mode", Utils.UrlEncode(item.headerType));
}
}
break;
}
return 0;
}
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
{
item.flow = query["flow"] ?? "";
item.streamSecurity = query["security"] ?? "";
item.sni = query["sni"] ?? "";
item.alpn = Utils.UrlDecode(query["alpn"] ?? "");
item.fingerprint = Utils.UrlDecode(query["fp"] ?? "");
item.publicKey = Utils.UrlDecode(query["pbk"] ?? "");
item.shortId = Utils.UrlDecode(query["sid"] ?? "");
item.spiderX = Utils.UrlDecode(query["spx"] ?? "");
item.network = query["type"] ?? nameof(ETransport.tcp);
switch (item.network)
{
case nameof(ETransport.tcp):
item.headerType = query["headerType"] ?? Global.None;
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
break;
case nameof(ETransport.kcp):
item.headerType = query["headerType"] ?? Global.None;
item.path = Utils.UrlDecode(query["seed"] ?? "");
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
case nameof(ETransport.splithttp):
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
item.path = Utils.UrlDecode(query["path"] ?? "/");
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
item.network = nameof(ETransport.h2);
item.requestHost = Utils.UrlDecode(query["host"] ?? "");
item.path = Utils.UrlDecode(query["path"] ?? "/");
break;
case nameof(ETransport.quic):
item.headerType = query["headerType"] ?? Global.None;
item.requestHost = query["quicSecurity"] ?? Global.None;
item.path = Utils.UrlDecode(query["key"] ?? "");
break;
case nameof(ETransport.grpc):
item.requestHost = Utils.UrlDecode(query["authority"] ?? "");
item.path = Utils.UrlDecode(query["serviceName"] ?? "");
item.headerType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode);
break;
default:
break;
}
return 0;
}
protected static bool Contains(string str, params string[] s)
{
foreach (var item in s)
{
if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}
protected static string WriteAllText(string strData, string ext = "json")
{
var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.{ext}");
File.WriteAllText(fileName, strData);
return fileName;
}
}
}

View File

@@ -0,0 +1,26 @@
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler.Fmt
{
internal class ClashFmt : BaseFmt
{
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "port", "socks-port", "proxies"))
{
var fileName = WriteAllText(strData, "yaml");
var profileItem = new ProfileItem
{
coreType = ECoreType.mihomo,
address = fileName,
remarks = subRemarks ?? "clash_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,94 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class FmtHandler
{
public static string? GetShareUri(ProfileItem item)
{
try
{
var url = item.configType switch
{
EConfigType.VMess => VmessFmt.ToUri(item),
EConfigType.Shadowsocks => ShadowsocksFmt.ToUri(item),
EConfigType.Socks => SocksFmt.ToUri(item),
EConfigType.Trojan => TrojanFmt.ToUri(item),
EConfigType.VLESS => VLESSFmt.ToUri(item),
EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item),
EConfigType.Tuic => TuicFmt.ToUri(item),
EConfigType.Wireguard => WireguardFmt.ToUri(item),
_ => null,
};
return url;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
return "";
}
}
public static ProfileItem? ResolveConfig(string config, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
try
{
string str = config.TrimEx();
if (Utils.IsNullOrEmpty(str))
{
msg = ResUI.FailedReadConfiguration;
return null;
}
if (str.StartsWith(Global.ProtocolShares[EConfigType.VMess]))
{
return VmessFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowsocks]))
{
return ShadowsocksFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Socks]))
{
return SocksFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Trojan]))
{
return TrojanFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.VLESS]))
{
return VLESSFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2]) || str.StartsWith(Global.Hysteria2ProtocolShare))
{
return Hysteria2Fmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Tuic]))
{
return TuicFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Wireguard]))
{
return WireguardFmt.Resolve(str, out msg);
}
else
{
msg = ResUI.NonvmessOrssProtocol;
return null;
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
msg = ResUI.Incorrectconfiguration;
return null;
}
}
}
}

View File

@@ -0,0 +1,104 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class Hysteria2Fmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
configType = EConfigType.Hysteria2
};
Uri url = new(str);
item.address = url.IdnHost;
item.port = url.Port;
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
item.path = Utils.UrlDecode(query["obfs-password"] ?? "");
item.allowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
var dicQuery = new Dictionary<string, string>();
if (!Utils.IsNullOrEmpty(item.sni))
{
dicQuery.Add("sni", item.sni);
}
if (!Utils.IsNullOrEmpty(item.alpn))
{
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
}
if (!Utils.IsNullOrEmpty(item.path))
{
dicQuery.Add("obfs", "salamander");
dicQuery.Add("obfs-password", Utils.UrlEncode(item.path));
}
dicQuery.Add("insecure", item.allowInsecure.ToLower() == "true" ? "1" : "0");
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
url = string.Format("{0}@{1}:{2}",
item.id,
GetIpv6(item.address),
item.port);
url = $"{Global.ProtocolShares[EConfigType.Hysteria2]}{url}/{query}{remark}";
return url;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "server", "up", "down", "listen", "<html>", "<body>"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
coreType = ECoreType.hysteria,
address = fileName,
remarks = subRemarks ?? "hysteria_custom"
};
return profileItem;
}
return null;
}
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
{
if (Contains(strData, "server", "auth", "up", "down", "listen"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
coreType = ECoreType.hysteria2,
address = fileName,
remarks = subRemarks ?? "hysteria2_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,26 @@
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler.Fmt
{
internal class NaiveproxyFmt : BaseFmt
{
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "listen", "proxy", "<html>", "<body>"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
coreType = ECoreType.naiveproxy,
address = fileName,
remarks = subRemarks ?? "naiveproxy_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,183 @@
using System.Text.RegularExpressions;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class ShadowsocksFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
item = ResolveSSLegacy(str) ?? ResolveSip002(str);
if (item == null)
{
return null;
}
if (item.address.Length == 0 || item.port == 0 || item.security.Length == 0 || item.id.Length == 0)
{
return null;
}
item.configType = EConfigType.Shadowsocks;
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
//url = string.Format("{0}:{1}@{2}:{3}",
// item.security,
// item.id,
// item.address,
// item.port);
//url = Utile.Base64Encode(url);
//new Sip002
var pw = Utils.Base64Encode($"{item.security}:{item.id}");
url = $"{pw}@{GetIpv6(item.address)}:{item.port}";
url = $"{Global.ProtocolShares[EConfigType.Shadowsocks]}{url}{remark}";
return url;
}
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex DetailsParser = new(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static ProfileItem? ResolveSSLegacy(string result)
{
var match = UrlFinder.Match(result);
if (!match.Success)
return null;
ProfileItem item = new();
var base64 = match.Groups["base64"].Value.TrimEnd('/');
var tag = match.Groups["tag"].Value;
if (!Utils.IsNullOrEmpty(tag))
{
item.remarks = Utils.UrlDecode(tag);
}
Match details;
try
{
details = DetailsParser.Match(Utils.Base64Decode(base64));
}
catch (FormatException)
{
return null;
}
if (!details.Success)
return null;
item.security = details.Groups["method"].Value;
item.id = details.Groups["password"].Value;
item.address = details.Groups["hostname"].Value;
item.port = Utils.ToInt(details.Groups["port"].Value);
return item;
}
private static ProfileItem? ResolveSip002(string result)
{
Uri parsedUrl;
try
{
parsedUrl = new Uri(result);
}
catch (UriFormatException)
{
return null;
}
ProfileItem item = new()
{
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
address = parsedUrl.IdnHost,
port = parsedUrl.Port,
};
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.UriEscaped);
//2022-blake3
if (rawUserInfo.Contains(':'))
{
string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
}
item.security = userInfoParts[0];
item.id = Utils.UrlDecode(userInfoParts[1]);
}
else
{
// parse base64 UserInfo
string userInfo = Utils.Base64Decode(rawUserInfo);
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
}
item.security = userInfoParts[0];
item.id = userInfoParts[1];
}
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
if (queryParameters["plugin"] != null)
{
//obfs-host exists
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
if (queryParameters["plugin"].Contains("obfs=http") && !Utils.IsNullOrEmpty(obfsHost))
{
obfsHost = obfsHost?.Replace("obfs-host=", "");
item.network = Global.DefaultNetwork;
item.headerType = Global.TcpHeaderHttp;
item.requestHost = obfsHost ?? "";
}
else
{
return null;
}
}
return item;
}
public static List<ProfileItem>? ResolveSip008(string result)
{
//SsSIP008
var lstSsServer = JsonUtils.Deserialize<List<SsServer>>(result);
if (lstSsServer?.Count <= 0)
{
var ssSIP008 = JsonUtils.Deserialize<SsSIP008>(result);
if (ssSIP008?.servers?.Count > 0)
{
lstSsServer = ssSIP008.servers;
}
}
if (lstSsServer?.Count > 0)
{
List<ProfileItem> lst = [];
foreach (var it in lstSsServer)
{
var ssItem = new ProfileItem()
{
remarks = it.remarks,
security = it.method,
id = it.password,
address = it.server,
port = Utils.ToInt(it.server_port)
};
lst.Add(ssItem);
}
return lst;
}
return null;
}
}
}

View File

@@ -0,0 +1,58 @@
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler.Fmt
{
internal class SingboxFmt : BaseFmt
{
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
if (configObjects != null && configObjects.Length > 0)
{
List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects)
{
var objectString = JsonUtils.Serialize(configObject);
var singboxCon = JsonUtils.Deserialize<SingboxConfig>(objectString);
if (singboxCon?.inbounds?.Count > 0
&& singboxCon.outbounds?.Count > 0
&& singboxCon.route != null)
{
var fileName = WriteAllText(objectString);
var profileIt = new ProfileItem
{
coreType = ECoreType.sing_box,
address = fileName,
remarks = subRemarks ?? "singbox_custom",
};
lstResult.Add(profileIt);
}
}
return lstResult;
}
return null;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(strData);
if (singboxConfig?.inbounds?.Count > 0
&& singboxConfig.outbounds?.Count > 0
&& singboxConfig.route != null)
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
coreType = ECoreType.sing_box,
address = fileName,
remarks = subRemarks ?? "singbox_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,131 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class SocksFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
item = ResolveSocksNew(str) ?? ResolveSocks(str);
if (item == null)
{
return null;
}
if (item.address.Length == 0 || item.port == 0)
{
return null;
}
item.configType = EConfigType.Socks;
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
//url = string.Format("{0}:{1}@{2}:{3}",
// item.security,
// item.id,
// item.address,
// item.port);
//url = Utile.Base64Encode(url);
//new
var pw = Utils.Base64Encode($"{item.security}:{item.id}");
url = $"{pw}@{GetIpv6(item.address)}:{item.port}";
url = $"{Global.ProtocolShares[EConfigType.Socks]}{url}{remark}";
return url;
}
private static ProfileItem? ResolveSocks(string result)
{
ProfileItem item = new()
{
configType = EConfigType.Socks
};
result = result[Global.ProtocolShares[EConfigType.Socks].Length..];
//remark
int indexRemark = result.IndexOf("#");
if (indexRemark > 0)
{
try
{
item.remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1));
}
catch { }
result = result[..indexRemark];
}
//part decode
int indexS = result.IndexOf("@");
if (indexS > 0)
{
}
else
{
result = Utils.Base64Decode(result);
}
string[] arr1 = result.Split('@');
if (arr1.Length != 2)
{
return null;
}
string[] arr21 = arr1[0].Split(':');
//string[] arr22 = arr1[1].Split(':');
int indexPort = arr1[1].LastIndexOf(":");
if (arr21.Length != 2 || indexPort < 0)
{
return null;
}
item.address = arr1[1][..indexPort];
item.port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
item.security = arr21[0];
item.id = arr21[1];
return item;
}
private static ProfileItem? ResolveSocksNew(string result)
{
Uri parsedUrl;
try
{
parsedUrl = new Uri(result);
}
catch (UriFormatException)
{
return null;
}
ProfileItem item = new()
{
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
address = parsedUrl.IdnHost,
port = parsedUrl.Port,
};
// parse base64 UserInfo
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped);
string userInfo = Utils.Base64Decode(rawUserInfo);
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length == 2)
{
item.security = userInfoParts[0];
item.id = userInfoParts[1];
}
return item;
}
}
}

View File

@@ -0,0 +1,53 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class TrojanFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
configType = EConfigType.Trojan
};
Uri url = new(str);
item.address = url.IdnHost;
item.port = url.Port;
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
var dicQuery = new Dictionary<string, string>();
GetStdTransport(item, null, ref dicQuery);
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
url = string.Format("{0}@{1}:{2}",
item.id,
GetIpv6(item.address),
item.port);
url = $"{Global.ProtocolShares[EConfigType.Trojan]}{url}{query}{remark}";
return url;
}
}
}

View File

@@ -0,0 +1,68 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class TuicFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
configType = EConfigType.Tuic
};
Uri url = new(str);
item.address = url.IdnHost;
item.port = url.Port;
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
var userInfoParts = url.UserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length == 2)
{
item.id = userInfoParts[0];
item.security = userInfoParts[1];
}
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
item.headerType = query["congestion_control"] ?? "";
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
var dicQuery = new Dictionary<string, string>();
if (!Utils.IsNullOrEmpty(item.sni))
{
dicQuery.Add("sni", item.sni);
}
if (!Utils.IsNullOrEmpty(item.alpn))
{
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
}
dicQuery.Add("congestion_control", item.headerType);
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
url = string.Format("{0}@{1}:{2}",
$"{item.id}:{item.security}",
GetIpv6(item.address),
item.port);
url = $"{Global.ProtocolShares[EConfigType.Tuic]}{url}{query}{remark}";
return url;
}
}
}

View File

@@ -0,0 +1,59 @@
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler.Fmt
{
internal class V2rayFmt : BaseFmt
{
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
if (configObjects != null && configObjects.Length > 0)
{
List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects)
{
var objectString = JsonUtils.Serialize(configObject);
var v2rayCon = JsonUtils.Deserialize<V2rayConfig>(objectString);
if (v2rayCon?.inbounds?.Count > 0
&& v2rayCon.outbounds?.Count > 0
&& v2rayCon.routing != null)
{
var fileName = WriteAllText(objectString);
var profileIt = new ProfileItem
{
coreType = ECoreType.Xray,
address = fileName,
remarks = v2rayCon.remarks ?? subRemarks ?? "v2ray_custom",
};
lstResult.Add(profileIt);
}
}
return lstResult;
}
return null;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(strData);
if (v2rayConfig?.inbounds?.Count > 0
&& v2rayConfig.outbounds?.Count > 0
&& v2rayConfig.routing != null)
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
coreType = ECoreType.Xray,
address = fileName,
remarks = v2rayConfig.remarks ?? subRemarks ?? "v2ray_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,64 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class VLESSFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
configType = EConfigType.VLESS,
security = Global.None
};
Uri url = new(str);
item.address = url.IdnHost;
item.port = url.Port;
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
item.security = query["encryption"] ?? Global.None;
item.streamSecurity = query["security"] ?? "";
ResolveStdTransport(query, ref item);
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
var dicQuery = new Dictionary<string, string>();
if (!Utils.IsNullOrEmpty(item.security))
{
dicQuery.Add("encryption", item.security);
}
else
{
dicQuery.Add("encryption", Global.None);
}
GetStdTransport(item, Global.None, ref dicQuery);
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
url = string.Format("{0}@{1}:{2}",
item.id,
GetIpv6(item.address),
item.port);
url = $"{Global.ProtocolShares[EConfigType.VLESS]}{url}{query}{remark}";
return url;
}
}
}

View File

@@ -0,0 +1,229 @@
using System.Text.RegularExpressions;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class VmessFmt : BaseFmt
{
private static readonly Regex StdVmessUserInfo = new(
@"^(?<network>[a-z]+)(\+(?<streamSecurity>[a-z]+))?:(?<id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$", RegexOptions.Compiled);
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
int indexSplit = str.IndexOf("?");
if (indexSplit > 0)
{
item = ResolveStdVmess(str) ?? ResolveVmess4Kitsunebi(str);
}
else
{
item = ResolveVmess(str, out msg);
}
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
VmessQRCode vmessQRCode = new()
{
v = item.configVersion,
ps = item.remarks.TrimEx(),
add = item.address,
port = item.port,
id = item.id,
aid = item.alterId,
scy = item.security,
net = item.network,
type = item.headerType,
host = item.requestHost,
path = item.path,
tls = item.streamSecurity,
sni = item.sni,
alpn = item.alpn,
fp = item.fingerprint
};
url = JsonUtils.Serialize(vmessQRCode);
url = Utils.Base64Encode(url);
url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}";
return url;
}
private static ProfileItem? ResolveVmess(string result, out string msg)
{
msg = string.Empty;
var item = new ProfileItem
{
configType = EConfigType.VMess
};
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
result = Utils.Base64Decode(result);
//转成Json
VmessQRCode? vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
if (vmessQRCode == null)
{
msg = ResUI.FailedConversionConfiguration;
return null;
}
item.network = Global.DefaultNetwork;
item.headerType = Global.None;
item.configVersion = Utils.ToInt(vmessQRCode.v);
item.remarks = Utils.ToString(vmessQRCode.ps);
item.address = Utils.ToString(vmessQRCode.add);
item.port = Utils.ToInt(vmessQRCode.port);
item.id = Utils.ToString(vmessQRCode.id);
item.alterId = Utils.ToInt(vmessQRCode.aid);
item.security = Utils.ToString(vmessQRCode.scy);
item.security = !Utils.IsNullOrEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
if (!Utils.IsNullOrEmpty(vmessQRCode.net))
{
item.network = vmessQRCode.net;
}
if (!Utils.IsNullOrEmpty(vmessQRCode.type))
{
item.headerType = vmessQRCode.type;
}
item.requestHost = Utils.ToString(vmessQRCode.host);
item.path = Utils.ToString(vmessQRCode.path);
item.streamSecurity = Utils.ToString(vmessQRCode.tls);
item.sni = Utils.ToString(vmessQRCode.sni);
item.alpn = Utils.ToString(vmessQRCode.alpn);
item.fingerprint = Utils.ToString(vmessQRCode.fp);
return item;
}
private static ProfileItem? ResolveStdVmess(string result)
{
ProfileItem item = new()
{
configType = EConfigType.VMess,
security = "auto"
};
Uri u = new(result);
item.address = u.IdnHost;
item.port = u.Port;
item.remarks = u.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
var query = Utils.ParseQueryString(u.Query);
var m = StdVmessUserInfo.Match(u.UserInfo);
if (!m.Success) return null;
item.id = m.Groups["id"].Value;
if (m.Groups["streamSecurity"].Success)
{
item.streamSecurity = m.Groups["streamSecurity"].Value;
}
switch (item.streamSecurity)
{
case Global.StreamSecurity:
break;
default:
if (!Utils.IsNullOrEmpty(item.streamSecurity))
return null;
break;
}
item.network = m.Groups["network"].Value;
switch (item.network)
{
case nameof(ETransport.tcp):
string t1 = query["type"] ?? Global.None;
item.headerType = t1;
break;
case nameof(ETransport.kcp):
item.headerType = query["type"] ?? Global.None;
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
case nameof(ETransport.splithttp):
string p1 = query["path"] ?? "/";
string h1 = query["host"] ?? "";
item.requestHost = Utils.UrlDecode(h1);
item.path = p1;
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
item.network = nameof(ETransport.h2);
string p2 = query["path"] ?? "/";
string h2 = query["host"] ?? "";
item.requestHost = Utils.UrlDecode(h2);
item.path = p2;
break;
case nameof(ETransport.quic):
string s = query["security"] ?? Global.None;
string k = query["key"] ?? "";
string t3 = query["type"] ?? Global.None;
item.headerType = t3;
item.requestHost = Utils.UrlDecode(s);
item.path = k;
break;
default:
return null;
}
return item;
}
private static ProfileItem? ResolveVmess4Kitsunebi(string result)
{
ProfileItem item = new()
{
configType = EConfigType.VMess
};
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
int indexSplit = result.IndexOf("?");
if (indexSplit > 0)
{
result = result[..indexSplit];
}
result = Utils.Base64Decode(result);
string[] arr1 = result.Split('@');
if (arr1.Length != 2)
{
return null;
}
string[] arr21 = arr1[0].Split(':');
string[] arr22 = arr1[1].Split(':');
if (arr21.Length != 2 || arr22.Length != 2)
{
return null;
}
item.address = arr22[0];
item.port = Utils.ToInt(arr22[1]);
item.security = arr21[0];
item.id = arr21[1];
item.network = Global.DefaultNetwork;
item.headerType = Global.None;
item.remarks = "Alien";
return item;
}
}
}

View File

@@ -0,0 +1,73 @@
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
namespace v2rayN.Handler.Fmt
{
internal class WireguardFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
configType = EConfigType.Wireguard
};
Uri url = new(str);
item.address = url.IdnHost;
item.port = url.Port;
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
item.publicKey = Utils.UrlDecode(query["publickey"] ?? "");
item.path = Utils.UrlDecode(query["reserved"] ?? "");
item.requestHost = Utils.UrlDecode(query["address"] ?? "");
item.shortId = Utils.UrlDecode(query["mtu"] ?? "");
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (!Utils.IsNullOrEmpty(item.remarks))
{
remark = "#" + Utils.UrlEncode(item.remarks);
}
var dicQuery = new Dictionary<string, string>();
if (!Utils.IsNullOrEmpty(item.publicKey))
{
dicQuery.Add("publickey", Utils.UrlEncode(item.publicKey));
}
if (!Utils.IsNullOrEmpty(item.path))
{
dicQuery.Add("reserved", Utils.UrlEncode(item.path));
}
if (!Utils.IsNullOrEmpty(item.requestHost))
{
dicQuery.Add("address", Utils.UrlEncode(item.requestHost));
}
if (!Utils.IsNullOrEmpty(item.shortId))
{
dicQuery.Add("mtu", Utils.UrlEncode(item.shortId));
}
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
url = string.Format("{0}@{1}:{2}",
Utils.UrlEncode(item.id),
GetIpv6(item.address),
item.port);
url = $"{Global.ProtocolShares[EConfigType.Wireguard]}{url}/{query}{remark}";
return url;
}
}
}

View File

@@ -4,6 +4,7 @@ using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;

View File

@@ -1,4 +1,5 @@
using System.Runtime.Intrinsics.X86;
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler
@@ -12,20 +13,26 @@ namespace v2rayN.Handler
public static LazyConfig Instance => _instance.Value;
private int? _statePort;
private int? _statePort2;
public int StatePort
{
get
{
if (_statePort is null)
{
_statePort = Utils.GetFreePort(GetLocalPort(EInboundProtocol.api));
}
_statePort ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api));
return _statePort.Value;
}
}
public int StatePort2
{
get
{
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
return _statePort2.Value;
}
}
private Job _processJob = new();
public LazyConfig()

View File

@@ -3,6 +3,8 @@ using Splat;
using System.Drawing;
using System.IO;
using System.Windows.Media.Imaging;
using v2rayN.Enums;
using v2rayN.Handler.CoreConfig;
using v2rayN.Models;
using v2rayN.Resx;

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;

View File

@@ -1,5 +1,6 @@
using System.Net.WebSockets;
using System.Text;
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler
@@ -27,7 +28,7 @@ namespace v2rayN.Handler
try
{
url = $"ws://{Global.Loopback}:{LazyConfig.Instance.StatePort}/traffic";
url = $"ws://{Global.Loopback}:{LazyConfig.Instance.StatePort2}/traffic";
if (webSocket == null)
{

View File

@@ -1,6 +1,7 @@
using Grpc.Core;
using Grpc.Net.Client;
using ProtosLib.Statistics;
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler
@@ -86,6 +87,8 @@ namespace v2rayN.Handler
private void ParseOutput(Google.Protobuf.Collections.RepeatedField<Stat> source, out ServerSpeedItem server)
{
server = new();
long aggregateProxyUp = 0;
long aggregateProxyDown = 0;
try
{
foreach (Stat stat in source)
@@ -100,15 +103,15 @@ namespace v2rayN.Handler
name = nStr[1];
type = nStr[3];
if (name == Global.ProxyTag)
if (name.StartsWith(Global.ProxyTag))
{
if (type == "uplink")
{
server.proxyUp = value;
aggregateProxyUp += value;
}
else if (type == "downlink")
{
server.proxyDown = value;
aggregateProxyDown += value;
}
}
else if (name == Global.DirectTag)
@@ -123,6 +126,8 @@ namespace v2rayN.Handler
}
}
}
server.proxyUp = aggregateProxyUp;
server.proxyDown = aggregateProxyDown;
}
catch
{

View File

@@ -1,4 +1,5 @@
using PacLib;
using v2rayN.Enums;
using v2rayN.Models;
namespace v2rayN.Handler

View File

@@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.Resx;
@@ -299,9 +300,6 @@ namespace v2rayN.Handler
{
await UpdateGeoFile("geosite", _config, update);
await UpdateGeoFile("geoip", _config, update);
//await UpdateGeoFile4Singbox("geosite", _config, false, update);
//await UpdateGeoFile4Singbox("geoip", _config, true, update);
});
}
@@ -327,7 +325,7 @@ namespace v2rayN.Handler
var result = await (new DownloadHandle()).DownloadStringAsync(url, true, "");
if (!Utils.IsNullOrEmpty(result))
{
responseHandler(type, result, preRelease);
ResponseHandler(type, result, preRelease);
}
else
{
@@ -345,7 +343,7 @@ namespace v2rayN.Handler
/// <summary>
/// 获取V2RayCore版本
/// </summary>
private SemanticVersion getCoreVersion(ECoreType type)
private SemanticVersion GetCoreVersion(ECoreType type)
{
try
{
@@ -410,7 +408,7 @@ namespace v2rayN.Handler
}
}
private void responseHandler(ECoreType type, string gitHubReleaseApi, bool preRelease)
private void ResponseHandler(ECoreType type, string gitHubReleaseApi, bool preRelease)
{
try
{
@@ -431,7 +429,7 @@ namespace v2rayN.Handler
case ECoreType.Xray:
case ECoreType.v2fly_v5:
{
curVersion = getCoreVersion(type);
curVersion = GetCoreVersion(type);
message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v"));
string osBit = "64";
switch (RuntimeInformation.ProcessArchitecture)
@@ -456,7 +454,7 @@ namespace v2rayN.Handler
case ECoreType.clash_meta:
case ECoreType.mihomo:
{
curVersion = getCoreVersion(type);
curVersion = GetCoreVersion(type);
message = string.Format(ResUI.IsLatestCore, type, curVersion);
switch (RuntimeInformation.ProcessArchitecture)
{
@@ -477,7 +475,7 @@ namespace v2rayN.Handler
}
case ECoreType.sing_box:
{
curVersion = getCoreVersion(type);
curVersion = GetCoreVersion(type);
message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v"));
switch (RuntimeInformation.ProcessArchitecture)
{

View File

@@ -1,4 +1,6 @@
namespace v2rayN.Models
using v2rayN.Enums;
namespace v2rayN.Models
{
/// <summary>
/// 本软件配置文件实体类

View File

@@ -1,4 +1,5 @@
using System.Windows.Input;
using v2rayN.Enums;
namespace v2rayN.Models
{
@@ -47,8 +48,8 @@ namespace v2rayN.Models
public bool udpEnabled { get; set; }
public bool sniffingEnabled { get; set; } = true;
public List<string>? destOverride { get; set; } = ["http", "tls"];
public bool routeOnly { get; set; }
public bool allowLANConn { get; set; }
public bool newPort4LAN { get; set; }
@@ -186,7 +187,7 @@ namespace v2rayN.Models
public string domainStrategy4Singbox { get; set; }
public string domainMatcher { get; set; }
public string routingIndexId { get; set; }
public bool enableRoutingAdvanced { get; set; }
public bool enableRoutingAdvanced { get; set; }
}
[Serializable]

View File

@@ -1,440 +0,0 @@
namespace v2rayN.Models
{
[Serializable]
public class ConfigOld
{
#region property
/// <summary>
/// 允许日志
/// </summary>
public bool logEnabled
{
get; set;
}
/// <summary>
/// 日志等级
/// </summary>
public string loglevel
{
get; set;
}
public string indexId
{
get; set;
}
/// <summary>
/// 允许Mux多路复用
/// </summary>
public bool muxEnabled
{
get; set;
}
/// <summary>
///
/// </summary>
public ESysProxyType sysProxyType
{
get; set;
}
/// <summary>
/// 启用实时网速和流量统计
/// </summary>
public bool enableStatistics
{
get; set;
}
/// <summary>
/// 去重时优先保留较旧(顶部)节点
/// </summary>
public bool keepOlderDedupl
{
get; set;
}
/// <summary>
/// 视图刷新率
/// </summary>
public int statisticsFreshRate
{
get; set;
}
/// <summary>
/// 自定义远程DNS
/// </summary>
public string remoteDNS
{
get; set;
}
/// <summary>
/// Outbound Freedom domainStrategy
/// </summary>
public string domainStrategy4Freedom
{
get; set;
}
/// <summary>
/// 是否允许不安全连接
/// </summary>
public bool defAllowInsecure
{
get; set;
}
/// <summary>
/// 域名解析策略
/// </summary>
public string domainStrategy
{
get; set;
}
public string domainMatcher
{
get; set;
}
public int routingIndex
{
get; set;
}
public bool enableRoutingAdvanced
{
get; set;
}
public bool ignoreGeoUpdateCore
{
get; set;
}
/// <summary>
/// systemProxyExceptions
/// </summary>
public string systemProxyExceptions
{
get; set;
}
public string systemProxyAdvancedProtocol { get; set; }
public int autoUpdateInterval { get; set; } = 0;
public int autoUpdateSubInterval { get; set; } = 0;
public bool checkPreReleaseUpdate { get; set; } = false;
public bool enableSecurityProtocolTls13
{
get; set;
}
public int trayMenuServersLimit { get; set; }
#endregion property
#region other entities
/// <summary>
/// 本地监听
/// </summary>
public List<InItem> inbound
{
get; set;
}
/// <summary>
/// vmess服务器信息
/// </summary>
public List<VmessItem> vmess
{
get; set;
}
/// <summary>
/// KcpItem
/// </summary>
public KcpItem kcpItem
{
get; set;
}
/// <summary>
/// 订阅
/// </summary>
public List<SubItem> subItem
{
get; set;
}
/// <summary>
/// UI
/// </summary>
public UIItem uiItem
{
get; set;
}
public List<RoutingItemOld> routings
{
get; set;
}
public ConstItem constItem
{
get; set;
}
public List<KeyEventItem> globalHotkeys
{
get; set;
}
public List<CoreTypeItem> coreTypeItem
{
get; set;
}
#endregion other entities
}
[Serializable]
public class VmessItem
{
public VmessItem()
{
indexId = string.Empty;
configType = EConfigType.VMess;
configVersion = 2;
sort = 0;
address = string.Empty;
port = 0;
id = string.Empty;
alterId = 0;
security = string.Empty;
network = string.Empty;
remarks = string.Empty;
headerType = string.Empty;
requestHost = string.Empty;
path = string.Empty;
streamSecurity = string.Empty;
allowInsecure = string.Empty;
testResult = string.Empty;
subid = string.Empty;
flow = string.Empty;
groupId = string.Empty;
}
public string indexId
{
get; set;
}
/// <summary>
/// config type(1=normal,2=custom)
/// </summary>
public EConfigType configType
{
get; set;
}
/// <summary>
/// 版本(现在=2)
/// </summary>
public int configVersion
{
get; set;
}
public int sort
{
get; set;
}
/// <summary>
/// 远程服务器地址
/// </summary>
public string address
{
get; set;
}
/// <summary>
/// 远程服务器端口
/// </summary>
public int port
{
get; set;
}
/// <summary>
/// 远程服务器ID
/// </summary>
public string id
{
get; set;
}
/// <summary>
/// 远程服务器额外ID
/// </summary>
public int alterId
{
get; set;
}
/// <summary>
/// 本地安全策略
/// </summary>
public string security
{
get; set;
}
/// <summary>
/// tcp,kcp,ws,h2,quic
/// </summary>
public string network
{
get; set;
}
/// <summary>
///
/// </summary>
public string remarks
{
get; set;
}
/// <summary>
/// 伪装类型
/// </summary>
public string headerType
{
get; set;
}
/// <summary>
/// 伪装的域名
/// </summary>
public string requestHost
{
get; set;
}
/// <summary>
/// ws h2 path
/// </summary>
public string path
{
get; set;
}
/// <summary>
/// 传输层安全
/// </summary>
public string streamSecurity
{
get; set;
}
/// <summary>
/// 是否允许不安全连接(用于客户端)
/// </summary>
public string allowInsecure
{
get; set;
}
/// <summary>
///
/// </summary>
public string testResult
{
get; set;
}
/// <summary>
/// SubItem id
/// </summary>
public string subid
{
get; set;
}
/// <summary>
/// VLESS flow
/// </summary>
public string flow
{
get; set;
}
/// <summary>
/// tls sni
/// </summary>
public string sni
{
get; set;
}
public string groupId
{
get; set;
} = string.Empty;
public ECoreType? coreType
{
get; set;
}
public int preSocksPort
{
get; set;
}
public string fingerprint { get; set; }
}
[Serializable]
public class RoutingItemOld
{
public string remarks
{
get; set;
}
public string url
{
get; set;
}
public List<RulesItem> rules
{
get; set;
}
public bool enabled { get; set; } = true;
public bool locked
{
get; set;
}
public string customIcon
{
get; set;
}
}
}

View File

@@ -1,4 +1,6 @@
namespace v2rayN.Models
using v2rayN.Enums;
namespace v2rayN.Models
{
[Serializable]
public class CoreInfo

View File

@@ -1,4 +1,5 @@
using SQLite;
using v2rayN.Enums;
namespace v2rayN.Models
{

View File

@@ -1,4 +1,5 @@
using SQLite;
using v2rayN.Enums;
namespace v2rayN.Models
{

View File

@@ -4,21 +4,22 @@
public class RulesItem
{
public string id { get; set; }
public string type { get; set; }
public string? type { get; set; }
public string port { get; set; }
public string? port { get; set; }
public string? network { get; set; }
public List<string> inboundTag { get; set; }
public List<string>? inboundTag { get; set; }
public string outboundTag { get; set; }
public string? outboundTag { get; set; }
public List<string> ip { get; set; }
public List<string>? ip { get; set; }
public List<string> domain { get; set; }
public List<string>? domain { get; set; }
public List<string> protocol { get; set; }
public List<string>? protocol { get; set; }
public List<string> process { get; set; }
public List<string>? process { get; set; }
public bool enabled { get; set; } = true;
}

View File

@@ -1,4 +1,6 @@
namespace v2rayN.Models
using v2rayN.Enums;
namespace v2rayN.Models
{
[Serializable]
internal class ServerTestItem

View File

@@ -28,6 +28,7 @@
public bool? disable_expire { get; set; }
public bool? independent_cache { get; set; }
public bool? reverse_mapping { get; set; }
public string? client_subnet { get; set; }
public Fakeip4Sbox? fakeip { get; set; }
}
@@ -44,12 +45,13 @@
public string? outbound { get; set; }
public string? server { get; set; }
public bool? disable_cache { get; set; }
public List<string>? inbound { get; set; }
public List<string>? protocol { get; set; }
public string? type { get; set; }
public string? mode { get; set; }
public string? network { get; set; }
public bool? ip_is_private { get; set; }
public string? client_subnet { get; set; }
public List<string>? inbound { get; set; }
public List<string>? protocol { get; set; }
public List<string>? network { get; set; }
public List<int>? port { get; set; }
public List<string>? port_range { get; set; }
public List<string>? geosite { get; set; }
@@ -62,6 +64,7 @@
public List<string>? source_ip_cidr { get; set; }
public List<string>? process_name { get; set; }
public List<string>? rule_set { get; set; }
public List<Rule4Sbox>? rules { get; set; }
}
[Serializable]
@@ -95,7 +98,7 @@
{
public string type { get; set; }
public string tag { get; set; }
public string server { get; set; }
public string? server { get; set; }
public int? server_port { get; set; }
public string uuid { get; set; }
public string security { get; set; }
@@ -184,11 +187,13 @@
public class Server4Sbox
{
public string tag { get; set; }
public string address { get; set; }
public string address_resolver { get; set; }
public string strategy { get; set; }
public string? tag { get; set; }
public string? address { get; set; }
public string? address_resolver { get; set; }
public string? address_strategy { get; set; }
public string? strategy { get; set; }
public string? detour { get; set; }
public string? client_subnet { get; set; }
}
public class Experimental4Sbox
@@ -241,6 +246,6 @@
public string? path { get; set; }
public string? url { get; set; }
public string? download_detour { get; set; }
public string? update_interval { get; set; }
public string? update_interval { get; set; }
}
}

View File

@@ -195,7 +195,7 @@ namespace v2rayN.Models
public class Sniffing4Ray
{
public bool enabled { get; set; }
public List<string> destOverride { get; set; }
public List<string>? destOverride { get; set; }
public bool routeOnly { get; set; }
}
@@ -394,6 +394,7 @@ namespace v2rayN.Models
public string? type { get; set; }
public string? port { get; set; }
public string? network { get; set; }
public List<string>? inboundTag { get; set; }
@@ -421,47 +422,52 @@ namespace v2rayN.Models
/// <summary>
///
/// </summary>
public TlsSettings4Ray tlsSettings { get; set; }
public TlsSettings4Ray? tlsSettings { get; set; }
/// <summary>
/// Tcp传输额外设置
/// </summary>
public TcpSettings4Ray tcpSettings { get; set; }
public TcpSettings4Ray? tcpSettings { get; set; }
/// <summary>
/// Kcp传输额外设置
/// </summary>
public KcpSettings4Ray kcpSettings { get; set; }
public KcpSettings4Ray? kcpSettings { get; set; }
/// <summary>
/// ws传输额外设置
/// </summary>
public WsSettings4Ray wsSettings { get; set; }
public WsSettings4Ray? wsSettings { get; set; }
/// <summary>
///
/// </summary>
public HttpupgradeSettings4Ray? httpupgradeSettings { get; set; }
/// <summary>
///
/// </summary>
public SplithttpSettings4Ray? splithttpSettings { get; set; }
/// <summary>
/// h2传输额外设置
/// </summary>
public HttpSettings4Ray httpSettings { get; set; }
public HttpSettings4Ray? httpSettings { get; set; }
/// <summary>
/// QUIC
/// </summary>
public QuicSettings4Ray quicSettings { get; set; }
public QuicSettings4Ray? quicSettings { get; set; }
/// <summary>
/// VLESS only
/// </summary>
public TlsSettings4Ray realitySettings { get; set; }
public TlsSettings4Ray? realitySettings { get; set; }
/// <summary>
/// grpc
/// </summary>
public GrpcSettings4Ray grpcSettings { get; set; }
public GrpcSettings4Ray? grpcSettings { get; set; }
/// <summary>
/// sockopt
@@ -488,7 +494,7 @@ namespace v2rayN.Models
public string? fingerprint { get; set; }
public bool? show { get; set; } = false;
public bool? show { get; set; }
public string? publicKey { get; set; }
public string? shortId { get; set; }
public string? spiderX { get; set; }
@@ -608,6 +614,17 @@ namespace v2rayN.Models
public string? host { get; set; }
}
public class SplithttpSettings4Ray
{
public string? path { get; set; }
public string? host { get; set; }
public int? maxUploadSize { get; set; }
public int? maxConcurrentUploads { get; set; }
}
public class HttpSettings4Ray
{
/// <summary>

View File

@@ -3266,7 +3266,7 @@ namespace v2rayN.Resx {
}
/// <summary>
/// 查找类似 *ws/httpupgrade path 的本地化字符串。
/// 查找类似 *ws/httpupgrade/splithttp path 的本地化字符串。
/// </summary>
public static string TransportPathTip1 {
get {
@@ -3320,7 +3320,7 @@ namespace v2rayN.Resx {
}
/// <summary>
/// 查找类似 *ws/httpupgrade host 的本地化字符串。
/// 查找类似 *ws/httpupgrade/splithttp host 的本地化字符串。
/// </summary>
public static string TransportRequestHostTip2 {
get {

View File

@@ -359,7 +359,7 @@
<value>Please fill in the correct custom DNS</value>
</data>
<data name="TransportPathTip1" xml:space="preserve">
<value>*ws/httpupgrade path</value>
<value>*ws/httpupgrade/splithttp path</value>
</data>
<data name="TransportPathTip2" xml:space="preserve">
<value>*h2 path</value>
@@ -374,7 +374,7 @@
<value>*http host Separated by commas (,)</value>
</data>
<data name="TransportRequestHostTip2" xml:space="preserve">
<value>*ws/httpupgrade host</value>
<value>*ws/httpupgrade/splithttp host</value>
</data>
<data name="TransportRequestHostTip3" xml:space="preserve">
<value>*h2 host Separated by commas (,)</value>

View File

@@ -359,7 +359,7 @@
<value>请填写正确的自定义DNS</value>
</data>
<data name="TransportPathTip1" xml:space="preserve">
<value>*ws/httpupgrade path</value>
<value>*ws/httpupgrade/splithttp path</value>
</data>
<data name="TransportPathTip2" xml:space="preserve">
<value>*h2 path</value>
@@ -374,7 +374,7 @@
<value>*http host中间逗号(,)分隔</value>
</data>
<data name="TransportRequestHostTip2" xml:space="preserve">
<value>*ws/httpupgrade host</value>
<value>*ws/httpupgrade/splithttp host</value>
</data>
<data name="TransportRequestHostTip3" xml:space="preserve">
<value>*h2 host中间逗号(,)分隔</value>

View File

@@ -358,7 +358,7 @@
<value>請填寫正確的自訂DNS</value>
</data>
<data name="TransportPathTip1" xml:space="preserve">
<value>*ws/httpupgrade path</value>
<value>*ws/httpupgrade/splithttp path</value>
</data>
<data name="TransportPathTip2" xml:space="preserve">
<value>*h2 path</value>
@@ -373,7 +373,7 @@
<value>*http host中間逗號(,)分隔</value>
</data>
<data name="TransportRequestHostTip2" xml:space="preserve">
<value>*ws/httpupgrade host</value>
<value>*ws/httpupgrade/splithttp host</value>
</data>
<data name="TransportRequestHostTip3" xml:space="preserve">
<value>*h2 host中間逗號(,)分隔</value>

View File

@@ -4,56 +4,7 @@
"error": "Verror.log",
"loglevel": "warning"
},
"inbounds": [{
"tag": "tag1",
"port": 10808,
"protocol": "socks",
"listen": "127.0.0.1",
"settings": {
"auth": "noauth",
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
},
{
"tag": "tag2",
"port": 10809,
"protocol": "http",
"listen": "127.0.0.1",
"settings": {
"allowTransparent": false
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
},
{
"tag": "tag3",
"port": 10809,
"protocol": "http",
"listen": "127.0.0.1",
"settings": {
"allowTransparent": false
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
],
"inbounds": [],
"outbounds": [{
"tag": "proxy",
"protocol": "vmess",

View File

@@ -3,14 +3,7 @@
"level": "debug",
"timestamp": true
},
"inbounds": [
{
"type": "socks",
"tag": "socks",
"listen": "127.0.0.1",
"listen_port": 10000
}
],
"inbounds": [],
"outbounds": [
{
"type": "vless",

View File

@@ -16,7 +16,22 @@
"domain": [
"geosite:geolocation-!cn",
"geosite:greatfire"
"geosite:greatfire"
]
},
{
"outboundTag": "proxy",
"ip": [
"1.0.0.1",
"1.1.1.1",
"8.8.8.8",
"8.8.4.4",
"geoip:facebook",
"geoip:fastly",
"geoip:google",
"geoip:netflix",
"geoip:telegram",
"geoip:twitter"
]
},
{

View File

@@ -22,6 +22,10 @@
{
"outboundTag": "direct",
"ip": [
"223.5.5.5/32",
"119.29.29.29/32",
"180.76.76.76/32",
"114.114.114.114/32",
"geoip:private",
"geoip:cn"
]

View File

@@ -30,5 +30,6 @@
],
"server": "block"
}
]
],
"final": "local"
}

View File

@@ -30,5 +30,6 @@
],
"server": "block"
}
]
],
"final": "local"
}

View File

@@ -3,6 +3,7 @@ using ReactiveUI.Fody.Helpers;
using Splat;
using System.Reactive;
using System.Windows;
using v2rayN.Enums;
using v2rayN.Handler;
using v2rayN.Models;
using v2rayN.Resx;

View File

@@ -3,6 +3,7 @@ using ReactiveUI.Fody.Helpers;
using Splat;
using System.Reactive;
using System.Windows;
using v2rayN.Enums;
using v2rayN.Handler;
using v2rayN.Models;
using v2rayN.Resx;

View File

@@ -14,7 +14,9 @@ using System.Reactive.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using v2rayN.Enums;
using v2rayN.Handler;
using v2rayN.Handler.Fmt;
using v2rayN.Models;
using v2rayN.Resx;
using v2rayN.Views;
@@ -1026,10 +1028,10 @@ namespace v2rayN.ViewModels
{
ShowHideWindow(false);
var dpiXY = Utils.GetDpiXY(Application.Current.MainWindow);
var dpiXY = QRCodeHelper.GetDpiXY(Application.Current.MainWindow);
string result = await Task.Run(() =>
{
return Utils.ScanScreen(dpiXY.Item1, dpiXY.Item2);
return QRCodeHelper.ScanScreen(dpiXY.Item1, dpiXY.Item2);
});
ShowHideWindow(true);
@@ -1152,7 +1154,7 @@ namespace v2rayN.ViewModels
_noticeHandler?.Enqueue(ResUI.PleaseSelectServer);
return;
}
var url = ShareHandler.GetShareUrl(item);
var url = FmtHandler.GetShareUri(item);
if (Utils.IsNullOrEmpty(url))
{
return;
@@ -1293,7 +1295,7 @@ namespace v2rayN.ViewModels
StringBuilder sb = new();
foreach (var it in lstSelecteds)
{
string url = ShareHandler.GetShareUrl(it);
string url = FmtHandler.GetShareUri(it);
if (Utils.IsNullOrEmpty(url))
{
continue;
@@ -1318,7 +1320,7 @@ namespace v2rayN.ViewModels
StringBuilder sb = new();
foreach (var it in lstSelecteds)
{
string? url = ShareHandler.GetShareUrl(it);
string? url = FmtHandler.GetShareUri(it);
if (Utils.IsNullOrEmpty(url))
{
continue;

View File

@@ -3,6 +3,7 @@ using ReactiveUI.Fody.Helpers;
using Splat;
using System.Reactive;
using System.Windows;
using v2rayN.Enums;
using v2rayN.Handler;
using v2rayN.Models;
using v2rayN.Resx;
@@ -20,6 +21,7 @@ namespace v2rayN.ViewModels
[Reactive] public int localPort { get; set; }
[Reactive] public bool udpEnabled { get; set; }
[Reactive] public bool sniffingEnabled { get; set; }
public IList<string> destOverride { get; set; }
[Reactive] public bool routeOnly { get; set; }
[Reactive] public bool allowLANConn { get; set; }
[Reactive] public bool newPort4LAN { get; set; }
@@ -262,7 +264,7 @@ namespace v2rayN.ViewModels
var needReboot = (EnableStatistics != _config.guiItem.enableStatistics
|| EnableDragDropSort != _config.uiItem.enableDragDropSort
|| EnableHWA != _config.guiItem.enableHWA
|| CurrentFontFamily != _config.uiItem.currentFontFamily);
|| CurrentFontFamily != _config.uiItem.currentFontFamily);
//if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString())
// || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString())
@@ -279,6 +281,7 @@ namespace v2rayN.ViewModels
_config.inbound[0].localPort = localPort;
_config.inbound[0].udpEnabled = udpEnabled;
_config.inbound[0].sniffingEnabled = sniffingEnabled;
_config.inbound[0].destOverride = destOverride?.ToList();
_config.inbound[0].routeOnly = routeOnly;
_config.inbound[0].allowLANConn = allowLANConn;
_config.inbound[0].newPort4LAN = newPort4LAN;

View File

@@ -4,6 +4,7 @@ using ReactiveUI.Fody.Helpers;
using Splat;
using System.Reactive;
using System.Windows;
using v2rayN.Enums;
using v2rayN.Handler;
using v2rayN.Models;
using v2rayN.Resx;
@@ -129,6 +130,7 @@ namespace v2rayN.ViewModels
id = item.id,
outboundTag = item.outboundTag,
port = item.port,
network = item.network,
protocols = Utils.List2String(item.protocol),
inboundTags = Utils.List2String(item.inboundTag),
domains = Utils.List2String(item.domain),
@@ -289,7 +291,7 @@ namespace v2rayN.ViewModels
private void ImportRulesFromClipboard()
{
string clipboardData = Utils.GetClipboardData();
var clipboardData = Utils.GetClipboardData();
if (AddBatchRoutingRules(SelectedRouting, clipboardData) == 0)
{
RefreshRulesItems();

View File

@@ -1,6 +1,7 @@
using ReactiveUI;
using System.Reactive.Disposables;
using System.Windows;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.ViewModels;

View File

@@ -2,6 +2,7 @@
using System.Reactive.Disposables;
using System.Windows;
using System.Windows.Controls;
using v2rayN.Enums;
using v2rayN.Handler;
using v2rayN.Models;
using v2rayN.Resx;
@@ -332,6 +333,7 @@ namespace v2rayN.Views
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
case nameof(ETransport.splithttp):
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
tipPath.Text = ResUI.TransportPathTip1;
break;

View File

@@ -51,7 +51,7 @@ namespace v2rayN.Views
private void linkDnsObjectDoc_Click(object sender, RoutedEventArgs e)
{
Utils.ProcessStart("https://www.v2fly.org/config/dns.html#dnsobject");
Utils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
}
private void linkDnsSingboxObjectDoc_Click(object sender, RoutedEventArgs e)

View File

@@ -2,6 +2,7 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using v2rayN.Enums;
using v2rayN.Handler;
using v2rayN.Models;
using v2rayN.Resx;

View File

@@ -38,6 +38,7 @@
<materialDesign:DialogHost
Identifier="RootDialog"
materialDesign:TransitionAssist.DisableTransitions="True"
SnackbarMessageQueue="{Binding ElementName=MainSnackbar, Path=MessageQueue}"
Style="{StaticResource MaterialDesignEmbeddedDialogHost}">
<Grid>

View File

@@ -10,6 +10,7 @@ using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using v2rayN.Base;
using v2rayN.Enums;
using v2rayN.Handler;
using v2rayN.Models;
using v2rayN.Resx;

View File

@@ -119,12 +119,22 @@
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsSniffingEnabled}" />
<ToggleButton
x:Name="togsniffingEnabled"
<StackPanel
Grid.Row="2"
Grid.Column="1"
Margin="{StaticResource SettingItemMargin}"
HorizontalAlignment="Left" />
Grid.ColumnSpan="2"
Orientation="Horizontal">
<ToggleButton
x:Name="togsniffingEnabled"
Margin="{StaticResource SettingItemMargin}"
HorizontalAlignment="Left" />
<ListBox
x:Name="clbdestOverride"
Margin="4"
HorizontalAlignment="Left"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFilterChipPrimaryListBox}" />
</StackPanel>
<TextBlock
Grid.Row="3"

View File

@@ -34,6 +34,15 @@ namespace v2rayN.Views
ViewModel = new OptionSettingViewModel(this);
clbdestOverride.SelectionChanged += ClbdestOverride_SelectionChanged;
Global.destOverrideProtocols.ForEach(it =>
{
clbdestOverride.Items.Add(it);
});
_config.inbound[0].destOverride?.ForEach(it =>
{
clbdestOverride.SelectedItems.Add(it);
});
Global.IEProxyProtocols.ForEach(it =>
{
cmbsystemProxyAdvancedProtocol.Items.Add(it);
@@ -213,5 +222,10 @@ namespace v2rayN.Views
}
return lstFonts;
}
private void ClbdestOverride_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
ViewModel.destOverride = clbdestOverride.SelectedItems.Cast<string>().ToList();
}
}
}

View File

@@ -125,16 +125,32 @@
Margin="4"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="network" />
<ComboBox
x:Name="cmbNetwork"
Grid.Row="4"
Grid.Column="1"
Width="200"
Margin="4"
MaxDropDownHeight="1000"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="4"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="enabled" />
<ToggleButton
x:Name="togEnabled"
Grid.Row="4"
Grid.Row="5"
Grid.Column="1"
Margin="4"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="4"
Grid.Row="5"
Grid.Column="2"
Margin="4"
HorizontalAlignment="Left"

View File

@@ -39,6 +39,10 @@ namespace v2rayN.Views
{
clbInboundTag.Items.Add(it);
});
Global.RuleNetworks.ForEach(it =>
{
cmbNetwork.Items.Add(it);
});
if (!rulesItem.id.IsNullOrEmpty())
{
@@ -56,6 +60,7 @@ namespace v2rayN.Views
{
this.Bind(ViewModel, vm => vm.SelectedSource.outboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.port, v => v.txtPort.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.network, v => v.cmbNetwork.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.enabled, v => v.togEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Domain, v => v.txtDomain.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.IP, v => v.txtIP.Text).DisposeWith(disposables);
@@ -83,7 +88,7 @@ namespace v2rayN.Views
private void linkRuleobjectDoc_Click(object sender, RoutedEventArgs e)
{
Utils.ProcessStart("https://www.v2fly.org/config/routing.html#ruleobject");
Utils.ProcessStart("https://xtls.github.io/config/routing.html#ruleobject");
}
}
}

View File

@@ -302,7 +302,7 @@
</DataGrid.ContextMenu>
<DataGrid.Columns>
<DataGridTextColumn
Width="120"
Width="110"
Binding="{Binding outboundTag}"
Header="outboundTag" />
<DataGridTextColumn
@@ -310,7 +310,7 @@
Binding="{Binding port}"
Header="port" />
<DataGridTextColumn
Width="100"
Width="80"
Binding="{Binding protocols}"
Header="protocol" />
<DataGridTextColumn
@@ -318,11 +318,15 @@
Binding="{Binding inboundTags}"
Header="inboundTag" />
<DataGridTextColumn
Width="220"
Width="90"
Binding="{Binding network}"
Header="network" />
<DataGridTextColumn
Width="190"
Binding="{Binding domains}"
Header="domain" />
<DataGridTextColumn
Width="220"
Width="190"
Binding="{Binding ips}"
Header="ip" />
<DataGridTextColumn

View File

@@ -2,6 +2,7 @@
using System.Reactive.Disposables;
using System.Windows;
using System.Windows.Input;
using v2rayN.Enums;
using v2rayN.Models;
using v2rayN.ViewModels;

View File

@@ -127,7 +127,7 @@ namespace v2rayN.Views
private void linkdomainStrategy_Click(object sender, System.Windows.RoutedEventArgs e)
{
Utils.ProcessStart("https://www.v2fly.org/config/routing.html");
Utils.ProcessStart("https://xtls.github.io/config/routing.html");
}
private void linkdomainStrategy4Singbox_Click(object sender, RoutedEventArgs e)

View File

@@ -23,7 +23,10 @@
TextOptions.TextRenderingMode="Auto"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<materialDesign:DialogHost Identifier="SubDialog" Style="{StaticResource MaterialDesignEmbeddedDialogHost}">
<materialDesign:DialogHost
materialDesign:TransitionAssist.DisableTransitions="True"
Identifier="SubDialog"
Style="{StaticResource MaterialDesignEmbeddedDialogHost}">
<DockPanel>
<ToolBarTray DockPanel.Dock="Top">
<ToolBar

View File

@@ -10,22 +10,22 @@
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationIcon>v2rayN.ico</ApplicationIcon>
<Copyright>Copyright © 2017-2024 (GPLv3)</Copyright>
<FileVersion>6.44</FileVersion>
<FileVersion>6.47</FileVersion>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Downloader" Version="3.0.6" />
<PackageReference Include="MaterialDesignThemes" Version="5.0.0" />
<PackageReference Include="MaterialDesignThemes" Version="5.1.0" />
<PackageReference Include="H.NotifyIcon.Wpf" Version="2.0.131" />
<PackageReference Include="QRCoder.Xaml" Version="1.5.1" />
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
<PackageReference Include="TaskScheduler" Version="2.11.0" />
<PackageReference Include="ZXing.Net.Bindings.Windows.Compatibility" Version="0.16.12" />
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageReference Include="ReactiveUI.Validation" Version="4.0.6" />
<PackageReference Include="ReactiveUI.WPF" Version="20.0.1" />
<PackageReference Include="Splat.NLog" Version="15.0.1" />
<PackageReference Include="ReactiveUI.Validation" Version="4.0.9" />
<PackageReference Include="ReactiveUI.WPF" Version="20.1.1" />
<PackageReference Include="Splat.NLog" Version="15.1.1" />
</ItemGroup>
<ItemGroup>