From 8e177a98512a7418ae4b00f02967f283f04faa5a Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 28 Jul 2025 23:28:20 +0800 Subject: [PATCH] Add Naive etc. uri share URI sharing is not compatible with other clients. Compatibility was not part of the original design and it is intended solely for internal sharing and copying within the application. --- v2rayN/ServiceLib/Global.cs | 12 +++- v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs | 49 +++++++++++++++ v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs | 20 ++++++ v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs | 63 +++++++++++++++++++ v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs | 52 +++++++++++++++ .../ServiceLib/Handler/Fmt/ShadowquicFmt.cs | 63 +++++++++++++++++++ 6 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs create mode 100644 v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs create mode 100644 v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs create mode 100644 v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index e1daa015..e7a2cd27 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -170,7 +170,11 @@ public class Global { EConfigType.Hysteria2, "hysteria2://" }, { EConfigType.TUIC, "tuic://" }, { EConfigType.WireGuard, "wireguard://" }, - { EConfigType.Anytls, "anytls://" } + { EConfigType.Anytls, "anytls://" }, + { EConfigType.NaiveProxy, "naive://" }, + { EConfigType.Juicity, "juicity://" }, + { EConfigType.Brook, "brook://" }, + { EConfigType.Shadowquic, "shadowquic://" } }; public static readonly Dictionary ProtocolTypes = new() @@ -184,7 +188,11 @@ public class Global { EConfigType.Hysteria2, "hysteria2" }, { EConfigType.TUIC, "tuic" }, { EConfigType.WireGuard, "wireguard" }, - { EConfigType.Anytls, "anytls" } + { EConfigType.Anytls, "anytls" }, + { EConfigType.NaiveProxy, "naiveproxy" }, + { EConfigType.Juicity, "juicity" }, + { EConfigType.Brook, "brook" }, + { EConfigType.Shadowquic, "shadowquic" } }; public static readonly List VmessSecurities = diff --git a/v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs new file mode 100644 index 00000000..4bde23b5 --- /dev/null +++ b/v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs @@ -0,0 +1,49 @@ +using static QRCoder.PayloadGenerator; + +namespace ServiceLib.Handler.Fmt; +public class BrookFmt : BaseFmt +{ + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + var parsedUrl = Utils.TryUri(str); + if (parsedUrl == null) + { + return null; + } + + ProfileItem item = new() + { + ConfigType = EConfigType.Brook, + Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), + Address = parsedUrl.IdnHost, + Port = parsedUrl.Port, + }; + var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); + item.Id = rawUserInfo; + + var query = Utils.ParseQueryString(parsedUrl.Query); + _ = ResolveStdTransport(query, ref item); + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) + { + return null; + } + var remark = string.Empty; + if (item.Remarks.IsNotEmpty()) + { + remark = "#" + Utils.UrlEncode(item.Remarks); + } + var pw = item.Id; + var dicQuery = new Dictionary(); + _ = GetStdTransport(item, Global.None, ref dicQuery); + + return ToUri(EConfigType.Brook, item.Address, item.Port, pw, dicQuery, remark); + } +} diff --git a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs index 814d753d..41e112ff 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs @@ -19,6 +19,10 @@ public class FmtHandler EConfigType.TUIC => TuicFmt.ToUri(item), EConfigType.WireGuard => WireguardFmt.ToUri(item), EConfigType.Anytls => AnytlsFmt.ToUri(item), + EConfigType.NaiveProxy => NaiveFmt.ToUri(item), + EConfigType.Juicity => JuicityFmt.ToUri(item), + EConfigType.Brook => BrookFmt.ToUri(item), + EConfigType.Shadowquic => ShadowquicFmt.ToUri(item), _ => null, }; @@ -80,6 +84,22 @@ public class FmtHandler { return AnytlsFmt.Resolve(str, out msg); } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.NaiveProxy])) + { + return NaiveFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Juicity])) + { + return JuicityFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Brook])) + { + return BrookFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowquic])) + { + return ShadowquicFmt.Resolve(str, out msg); + } else { msg = ResUI.NonvmessOrssProtocol; diff --git a/v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs new file mode 100644 index 00000000..d6cda203 --- /dev/null +++ b/v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs @@ -0,0 +1,63 @@ +namespace ServiceLib.Handler.Fmt; + +public class JuicityFmt : BaseFmt +{ + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + ProfileItem item = new() + { + ConfigType = EConfigType.Juicity + }; + + var url = Utils.TryUri(str); + if (url == null) + { + return null; + } + + item.Address = url.IdnHost; + item.Port = url.Port; + item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + var rawUserInfo = Utils.UrlDecode(url.UserInfo); + var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); + if (userInfoParts.Length == 2) + { + item.Id = userInfoParts.First(); + item.Security = userInfoParts.Last(); + } + + 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; + } + + var remark = string.Empty; + if (item.Remarks.IsNotEmpty()) + { + remark = "#" + Utils.UrlEncode(item.Remarks); + } + var dicQuery = new Dictionary(); + if (item.Sni.IsNotEmpty()) + { + dicQuery.Add("sni", item.Sni); + } + if (item.Alpn.IsNotEmpty()) + { + dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn)); + } + dicQuery.Add("congestion_control", item.HeaderType); + + return ToUri(EConfigType.Juicity, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark); + } +} diff --git a/v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs new file mode 100644 index 00000000..95c5583d --- /dev/null +++ b/v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs @@ -0,0 +1,52 @@ +using static QRCoder.PayloadGenerator; + +namespace ServiceLib.Handler.Fmt; +public class NaiveFmt : BaseFmt +{ + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + var parsedUrl = Utils.TryUri(str); + if (parsedUrl == null) + { + return null; + } + + ProfileItem item = new() + { + ConfigType = EConfigType.NaiveProxy, + Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), + Address = parsedUrl.IdnHost, + Port = parsedUrl.Port, + }; + var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); + item.Id = rawUserInfo; + + var query = Utils.ParseQueryString(parsedUrl.Query); + _ = ResolveStdTransport(query, ref item); + + item.HeaderType = query["protocol"] ?? ""; + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) + { + return null; + } + var remark = string.Empty; + if (item.Remarks.IsNotEmpty()) + { + remark = "#" + Utils.UrlEncode(item.Remarks); + } + var pw = item.Id; + var dicQuery = new Dictionary(); + _ = GetStdTransport(item, Global.None, ref dicQuery); + dicQuery.Add("protocol", item.HeaderType); + + return ToUri(EConfigType.NaiveProxy, item.Address, item.Port, pw, dicQuery, remark); + } +} diff --git a/v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs new file mode 100644 index 00000000..468923da --- /dev/null +++ b/v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs @@ -0,0 +1,63 @@ +namespace ServiceLib.Handler.Fmt; + +public class ShadowquicFmt : BaseFmt +{ + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + ProfileItem item = new() + { + ConfigType = EConfigType.Shadowquic + }; + + var url = Utils.TryUri(str); + if (url == null) + { + return null; + } + + item.Address = url.IdnHost; + item.Port = url.Port; + item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + var rawUserInfo = Utils.UrlDecode(url.UserInfo); + var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); + if (userInfoParts.Length == 2) + { + item.Id = userInfoParts.First(); + item.Security = userInfoParts.Last(); + } + + 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; + } + + var remark = string.Empty; + if (item.Remarks.IsNotEmpty()) + { + remark = "#" + Utils.UrlEncode(item.Remarks); + } + var dicQuery = new Dictionary(); + if (item.Sni.IsNotEmpty()) + { + dicQuery.Add("sni", item.Sni); + } + if (item.Alpn.IsNotEmpty()) + { + dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn)); + } + dicQuery.Add("congestion_control", item.HeaderType); + + return ToUri(EConfigType.Shadowquic, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark); + } +}